diff --git a/.gitignore b/.gitignore index 6c7dbb9d..4d389dc6 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ obj/ [Rr]elease*/ _ReSharper*/ SIMPLSharpLogs/ -*.projectinfo \ No newline at end of file +*.projectinfo +essentials-framework/EssentialDMTestConfig/ diff --git a/.gitmodules b/.gitmodules index 2ae0902f..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "essentials-framework"] - path = essentials-framework - url = https://bitbucket.org/Pepperdash_Products/essentials-framework.git diff --git a/essentials-framework b/essentials-framework deleted file mode 160000 index 2e132f83..00000000 --- a/essentials-framework +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2e132f830f947c62ca148d7ce8569b6a3d604cab diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase.sln b/essentials-framework/Essentials Core/PepperDashEssentialsBase.sln new file mode 100644 index 00000000..a51c8c5d --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash_Essentials_Core", "PepperDashEssentialsBase\PepperDash_Essentials_Core.csproj", "{A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CecPortController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CecPortController.cs new file mode 100644 index 00000000..a2493e3c --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CecPortController.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public class CecPortController : Device, IBasicCommunication + { + public event EventHandler BytesReceived; + public event EventHandler TextReceived; + + public bool IsConnected { get { return true; } } + + ICec Port; + + public CecPortController(string key, ICec port) + : base(key) + { + Port = port; + + Port.StreamCec.CecChange += new CecChangeEventHandler(StreamCec_CecChange); + } + + void StreamCec_CecChange(Cec cecDevice, CecEventArgs args) + { + if (args.EventId == CecEventIds.CecMessageReceivedEventId) + OnDataReceived(cecDevice.Received.StringValue); + else if (args.EventId == CecEventIds.ErrorFeedbackEventId) + if(cecDevice.ErrorFeedback.BoolValue) + Debug.Console(2, this, "CEC NAK Error"); + } + + void OnDataReceived(string s) + { + var bytesHandler = BytesReceived; + if (bytesHandler != null) + { + var bytes = Encoding.GetEncoding(28591).GetBytes(s); + bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); + } + var textHandler = TextReceived; + if (textHandler != null) + textHandler(this, new GenericCommMethodReceiveTextArgs(s)); + } + + #region IBasicCommunication Members + + public void SendText(string text) + { + if (Port == null) + return; + Port.StreamCec.Send.StringValue = text; + } + + public void SendBytes(byte[] bytes) + { + if (Port == null) + return; + var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); + Port.StreamCec.Send.StringValue = text; + } + + public void Connect() + { + } + + public void Disconnect() + { + } + + #endregion + + /// + /// + /// + /// + public void SimulateReceive(string s) + { + // split out hex chars and build string + var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])"); + StringBuilder b = new StringBuilder(); + foreach (var t in split) + { + if (t.StartsWith(@"\") && t.Length == 4) + b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16))); + else + b.Append(t); + } + + OnDataReceived(b.ToString()); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs new file mode 100644 index 00000000..a5a5b14f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs @@ -0,0 +1,132 @@ +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; + + +namespace PepperDash.Essentials.Core +{ + public class ComPortController : Device, IBasicCommunication + { + public event EventHandler BytesReceived; + public event EventHandler TextReceived; + + public bool IsConnected { get { return true; } } + + ComPort Port; + ComPort.ComPortSpec Spec; + + public ComPortController(string key, ComPort port, ComPort.ComPortSpec spec) + : base(key) + { + if (port == null) + { + Debug.Console(0, this, "ERROR: Invalid com port, continuing but comms will not function"); + return; + } + + Port = port; + Spec = spec; + //IsConnected = new BoolFeedback(CommonBoolCue.IsConnected, () => true); + + if (Port.Parent is CrestronControlSystem) + { + + + var result = Port.Register(); + if (result != eDeviceRegistrationUnRegistrationResponse.Success) + { + Debug.Console(0, this, "ERROR: Cannot register Com port: {0}", result); + return; // false + } + } + var specResult = Port.SetComPortSpec(Spec); + if (specResult != 0) + { + Debug.Console(0, this, "WARNING: Cannot set comspec"); + return; // false + } + Port.SerialDataReceived += new ComPortDataReceivedEvent(Port_SerialDataReceived); + } + + ~ComPortController() + { + Port.SerialDataReceived -= Port_SerialDataReceived; + } + + void Port_SerialDataReceived(ComPort ReceivingComPort, ComPortSerialDataEventArgs args) + { + OnDataReceived(args.SerialData); + } + + void OnDataReceived(string s) + { + var bytesHandler = BytesReceived; + if (bytesHandler != null) + { + var bytes = Encoding.GetEncoding(28591).GetBytes(s); + bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); + } + var textHandler = TextReceived; + if (textHandler != null) + textHandler(this, new GenericCommMethodReceiveTextArgs(s)); + } + + public override bool Deactivate() + { + return Port.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success; + } + + #region IBasicCommunication Members + + public void SendText(string text) + { + if (Port == null) + return; + Port.Send(text); + } + + public void SendBytes(byte[] bytes) + { + if (Port == null) + return; + var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); + Port.Send(text); + } + + public void Connect() + { + } + + public void Disconnect() + { + } + + #endregion + + /// + /// + /// + /// + public void SimulateReceive(string s) + { + // split out hex chars and build string + var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])"); + StringBuilder b = new StringBuilder(); + foreach (var t in split) + { + if (t.StartsWith(@"\") && t.Length == 4) + b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16))); + else + b.Append(t); + } + + OnDataReceived(b.ToString()); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComSpecJsonConverter.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComSpecJsonConverter.cs new file mode 100644 index 00000000..fe77bf83 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComSpecJsonConverter.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + /// + /// This converter creates a proper ComPort.ComPortSpec struct from more-friendly JSON values. It uses + /// ComSpecPropsJsonConverter to finish the individual properties. + /// + public class ComSpecJsonConverter : JsonConverter + { + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (objectType == typeof(ComPort.ComPortSpec)) + { + var newSer = new JsonSerializer(); + newSer.Converters.Add(new ComSpecPropsJsonConverter()); + newSer.ObjectCreationHandling = ObjectCreationHandling.Replace; + return newSer.Deserialize(reader); + } + return null; + } + + /// + /// + /// + public override bool CanConvert(Type objectType) + { + return objectType == typeof(ComPort.ComPortSpec); + } + + public override bool CanRead { get { return true; } } + + /// + /// This converter will not be used for writing + /// + public override bool CanWrite { get { return false; } } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + + /// + /// The gist of this converter: The comspec JSON comes in with normal values that need to be converted + /// into enum names. This converter takes the value and applies the appropriate enum's name prefix to the value + /// and then returns the enum value using Enum.Parse. NOTE: Does not write + /// + public class ComSpecPropsJsonConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(ComPort.eComBaudRates) + || objectType == typeof(ComPort.eComDataBits) + || objectType == typeof(ComPort.eComParityType) + || objectType == typeof(ComPort.eComHardwareHandshakeType) + || objectType == typeof(ComPort.eComSoftwareHandshakeType) + || objectType == typeof(ComPort.eComProtocolType) + || objectType == typeof(ComPort.eComStopBits); + } + + public override bool CanRead { get { return true; } } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + //Debug.Console(2, "ReadJson type: " + objectType.Name); + if (objectType == typeof(ComPort.eComBaudRates)) + return Enum.Parse(typeof(ComPort.eComBaudRates), "ComspecBaudRate" + reader.Value, false); + else if (objectType == typeof(ComPort.eComDataBits)) + return Enum.Parse(typeof(ComPort.eComDataBits), "ComspecDataBits" + reader.Value, true); + else if (objectType == typeof(ComPort.eComHardwareHandshakeType)) + return Enum.Parse(typeof(ComPort.eComHardwareHandshakeType), "ComspecHardwareHandshake" + reader.Value, true); + else if (objectType == typeof(ComPort.eComParityType)) + return Enum.Parse(typeof(ComPort.eComParityType), "ComspecParity" + reader.Value, true); + else if (objectType == typeof(ComPort.eComProtocolType)) + return Enum.Parse(typeof(ComPort.eComProtocolType), "ComspecProtocol" + reader.Value, true); + else if (objectType == typeof(ComPort.eComSoftwareHandshakeType)) + return Enum.Parse(typeof(ComPort.eComSoftwareHandshakeType), "ComspecSoftwareHandshake" + reader.Value, true); + else if (objectType == typeof(ComPort.eComStopBits)) + return Enum.Parse(typeof(ComPort.eComStopBits), "ComspecStopBits" + reader.Value, true); + return null; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs new file mode 100644 index 00000000..23aea272 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs @@ -0,0 +1,196 @@ +using System; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public class CommFactory + { + public static EssentialsControlPropertiesConfig GetControlPropertiesConfig(DeviceConfig deviceConfig) + { + try + { + return JsonConvert.DeserializeObject + (deviceConfig.Properties["control"].ToString()); + //Debug.Console(2, "Control TEST: {0}", JsonConvert.SerializeObject(controlConfig)); + } + catch (Exception e) + { + + Debug.Console(0, "ERROR: [{0}] Control properties deserialize failed:\r{1}", deviceConfig.Key, e); + return null; + } + } + + + /// + /// Returns a comm method of either com port, TCP, SSH, and puts this into the DeviceManager + /// + /// The Device config object + public static IBasicCommunication CreateCommForDevice(DeviceConfig deviceConfig) + { + EssentialsControlPropertiesConfig controlConfig = GetControlPropertiesConfig(deviceConfig); + if (controlConfig == null) + return null; + + IBasicCommunication comm = null; + try + { + var c = controlConfig.TcpSshProperties; + switch (controlConfig.Method) + { + case eControlMethod.Com: + comm = new ComPortController(deviceConfig.Key + "-com", GetComPort(controlConfig), controlConfig.ComParams); + break; + case eControlMethod.Cec: + comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort(controlConfig)); + break; + case eControlMethod.IR: + break; + case eControlMethod.Ssh: + { + var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password); + ssh.AutoReconnect = c.AutoReconnect; + if(ssh.AutoReconnect) + ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs; + comm = ssh; + break; + } + case eControlMethod.Tcpip: + { + var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize); + tcp.AutoReconnect = c.AutoReconnect; + if (tcp.AutoReconnect) + tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs; + comm = tcp; + break; + } + case eControlMethod.Udp: + { + var udp = new GenericUdpServer(deviceConfig.Key + "-udp", c.Address, c.Port, c.BufferSize); + comm = udp; + break; + } + case eControlMethod.Telnet: + break; + default: + break; + } + } + catch (Exception e) + { + Debug.Console(0, "Cannot create communication from JSON:\r{0}\r\rException:\r{1}", + deviceConfig.Properties.ToString(), e); + } + + // put it in the device manager if it's the right flavor + var comDev = comm as Device; + if (comDev != null) + DeviceManager.AddDevice(comDev); + return comm; + } + + public static ComPort GetComPort(EssentialsControlPropertiesConfig config) + { + var comPar = config.ComParams; + var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey); + if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts) + return dev.ComPorts[config.ControlPortNumber]; + Debug.Console(0, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber); + return null; + } + + /// + /// Gets an ICec port from a RoutingInput or RoutingOutput on a device + /// + /// + /// + public static ICec GetCecPort(ControlPropertiesConfig config) + { + var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey); + + if (dev != null) + { + var inputPort = (dev as IRoutingInputsOutputs).InputPorts[config.ControlPortName]; + + if (inputPort != null) + if (inputPort.Port is ICec) + return inputPort.Port as ICec; + + var outputPort = (dev as IRoutingInputsOutputs).OutputPorts[config.ControlPortName]; + + if (outputPort != null) + if (outputPort.Port is ICec) + return outputPort.Port as ICec; + } + Debug.Console(0, "GetCecPort: Device '{0}' does not have a CEC port called: '{1}'", config.ControlPortDevKey, config.ControlPortName); + return null; + } + + /// + /// Helper to grab the IComPorts device for this PortDeviceKey. Key "controlSystem" will + /// return the ControlSystem object from the Global class. + /// + /// IComPorts device or null if the device is not found or does not implement IComPorts + public static IComPorts GetIComPortsDeviceFromManagedDevice(string ComPortDevKey) + { + if ((ComPortDevKey.Equals("controlSystem", System.StringComparison.OrdinalIgnoreCase) + || ComPortDevKey.Equals("processor", System.StringComparison.OrdinalIgnoreCase)) + && Global.ControlSystem is IComPorts) + return Global.ControlSystem; + else + { + var dev = DeviceManager.GetDeviceForKey(ComPortDevKey) as IComPorts; + if (dev == null) + Debug.Console(0, "ComPortConfig: Cannot find com port device '{0}'", ComPortDevKey); + return dev; + } + } + } + + /// + /// + /// + public class EssentialsControlPropertiesConfig : + PepperDash.Core.ControlPropertiesConfig + { + + [JsonConverter(typeof(ComSpecJsonConverter))] + public ComPort.ComPortSpec ComParams { get; set; } + + public string CresnetId { get; set; } + + /// + /// Attempts to provide uint conversion of string CresnetId + /// + public uint CresnetIdInt + { + get + { + try + { + return Convert.ToUInt32(CresnetId, 16); + } + catch (Exception) + { + throw new FormatException(string.Format("ERROR:Unable to convert Cresnet ID: {0} to hex. Error:\n{1}", CresnetId)); + } + } + } + } + + public class IrControlSpec + { + public string PortDeviceKey { get; set; } + public uint PortNumber { get; set; } + public string File { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommunicationExtras.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommunicationExtras.cs new file mode 100644 index 00000000..05fb3954 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommunicationExtras.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public interface IComPortsDevice + { + IComPorts Device { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ConsoleCommMockDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ConsoleCommMockDevice.cs new file mode 100644 index 00000000..d833aafa --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ConsoleCommMockDevice.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public class ConsoleCommMockDevice : Device, ICommunicationMonitor + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + + /// + /// Defaults to \x0a + /// + public string LineEnding { get; set; } + + /// + /// Set to true to show responses in full hex + /// + public bool ShowHexResponse { get; set; } + + public ConsoleCommMockDevice(string key, string name, ConsoleCommMockDevicePropertiesConfig props, IBasicCommunication comm) + :base(key, name) + { + Communication = comm; + PortGather = new CommunicationGather(Communication, '\x0d'); + PortGather.LineReceived += this.Port_LineReceived; + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + LineEnding = props.LineEnding; + } + + public override bool CustomActivate() + { + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + + CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + return true; + } + + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + if (Debug.Level == 2) + Debug.Console(2, this, "RX: '{0}'", + ShowHexResponse ? ComTextHelper.GetEscapedText(args.Text) : args.Text); + } + + void SendLine(string s) + { + //if (Debug.Level == 2) + // Debug.Console(2, this, " Send '{0}'", ComTextHelper.GetEscapedText(s)); + Communication.SendText(s + LineEnding); + } + } + + public class ConsoleCommMockDevicePropertiesConfig + { + public string LineEnding { get; set; } + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public ConsoleCommMockDevicePropertiesConfig() + { + LineEnding = "\x0a"; + } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/DELETE ComPortController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/DELETE ComPortController.cs new file mode 100644 index 00000000..ece72b20 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/DELETE ComPortController.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core +{ + public class ComPortController : Device, IBasicCommunication + { + public event EventHandler BytesReceived; + public event EventHandler TextReceived; + + ComPort Port; + ComPort.ComPortSpec Spec; + + public ComPortController(string key, IComPorts ComDevice, uint comPortNum, ComPort.ComPortSpec spec) + : base(key) + { + Port = ComDevice.ComPorts[comPortNum]; + Spec = spec; + + Debug.Console(2, "Creating com port '{0}'", key); + Debug.Console(2, "Com port spec:\r{0}", JsonConvert.SerializeObject(spec)); + } + + /// + /// Creates a ComPort if the parameters are correct. Returns and logs errors if not + /// + public static ComPortController GetComPortController(string key, + IComPorts comDevice, uint comPortNum, ComPort.ComPortSpec spec) + { + Debug.Console(1, "Creating com port '{0}'", key); + if (comDevice == null) + throw new ArgumentNullException("comDevice"); + if (string.IsNullOrEmpty(key)) + throw new ArgumentNullException("key"); + if (comPortNum > comDevice.NumberOfComPorts) + { + Debug.Console(0, "[{0}] Com port {1} out of range on {2}", + key, comPortNum, comDevice.GetType().Name); + return null; + } + var port = new ComPortController(key, comDevice, comPortNum, spec); + return port; + } + + /// + /// Registers port and sends ComSpec + /// + /// false if either register or comspec fails + public override bool CustomActivate() + { + var result = Port.Register(); + if (result != eDeviceRegistrationUnRegistrationResponse.Success) + { + Debug.Console(0, this, "Cannot register Com port: {0}", result); + return false; + } + var specResult = Port.SetComPortSpec(Spec); + if (specResult != 0) + { + Debug.Console(0, this, "Cannot set comspec"); + return false; + } + Port.SerialDataReceived += new ComPortDataReceivedEvent(Port_SerialDataReceived); + return true; + } + + void Port_SerialDataReceived(ComPort ReceivingComPort, ComPortSerialDataEventArgs args) + { + if (BytesReceived != null) + { + var bytes = Encoding.GetEncoding(28591).GetBytes(args.SerialData); + BytesReceived(this, new GenericCommMethodReceiveBytesArgs(bytes)); + } + if(TextReceived != null) + TextReceived(this, new GenericCommMethodReceiveTextArgs(args.SerialData)); + } + + public override bool Deactivate() + { + return Port.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success; + } + + #region IBasicCommunication Members + + public void SendText(string text) + { + Port.Send(text); + } + + public void SendBytes(byte[] bytes) + { + + var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); + Port.Send(text); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/GenericComm.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/GenericComm.cs new file mode 100644 index 00000000..1998f62e --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/GenericComm.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; + +using PepperDash.Core; +using PepperDash.Essentials.Core.Devices; +using PepperDash.Essentials.Core.Config; + + +namespace PepperDash.Essentials.Core +{ + /// + /// Serves as a generic wrapper class for all styles of IBasicCommuncation ports + /// + public class + GenericComm : ReconfigurableDevice + { + EssentialsControlPropertiesConfig PropertiesConfig; + + public IBasicCommunication CommPort { get; private set; } + + public GenericComm(DeviceConfig config) + : base(config) + { + PropertiesConfig = CommFactory.GetControlPropertiesConfig(config); + + CommPort = CommFactory.CreateCommForDevice(config); + } + + public void SetPortConfig(string portConfig) + { + // TODO: Deserialize new EssentialsControlPropertiesConfig and handle as necessary + try + { + PropertiesConfig = JsonConvert.DeserializeObject + (portConfig.ToString()); + } + catch (Exception e) + { + Debug.Console(2, this, "Error deserializing port config: {0}", e); + } + } + + protected override void CustomSetConfig(DeviceConfig config) + { + PropertiesConfig = CommFactory.GetControlPropertiesConfig(config); + + ConfigWriter.UpdateDeviceConfig(config); + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/IRPortHelper.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/IRPortHelper.cs new file mode 100644 index 00000000..015b03ea --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/IRPortHelper.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharpPro; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public static class IRPortHelper + { + public static string IrDriverPathPrefix + { + get + { + return Global.FilePathPrefix + "IR" + Global.DirectorySeparator; + } + } + + /// + /// Finds either the ControlSystem or a device controller that contains IR ports and + /// returns a port from the hardware device + /// + /// + /// IrPortConfig object. The port and or filename will be empty/null + /// if valid values don't exist on config + public static IrOutPortConfig GetIrPort(JToken propsToken) + { + var control = propsToken["control"]; + if (control == null) + return null; + if (control["method"].Value() != "ir") + { + Debug.Console(0, "IRPortHelper called with non-IR properties"); + return null; + } + + var port = new IrOutPortConfig(); + + var portDevKey = control.Value("controlPortDevKey"); + var portNum = control.Value("controlPortNumber"); + if (portDevKey == null || portNum == 0) + { + Debug.Console(1, "WARNING: Properties is missing port device or port number"); + return port; + } + + IIROutputPorts irDev = null; + if (portDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase) + || portDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase)) + irDev = Global.ControlSystem; + else + irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts; + + if (irDev == null) + { + Debug.Console(1, "[Config] Error, device with IR ports '{0}' not found", portDevKey); + return port; + } + + if (portNum <= irDev.NumberOfIROutputPorts) // success! + { + var file = IrDriverPathPrefix + control["irFile"].Value(); + port.Port = irDev.IROutputPorts[portNum]; + port.FileName = file; + return port; // new IrOutPortConfig { Port = irDev.IROutputPorts[portNum], FileName = file }; + } + else + { + Debug.Console(1, "[Config] Error, device '{0}' IR port {1} out of range", + portDevKey, portNum); + return port; + } + } + + /// + /// Returns a ready-to-go IrOutputPortController from a DeviceConfig object. + /// + public static IrOutputPortController GetIrOutputPortController(DeviceConfig devConf) + { + var irControllerKey = devConf.Key + "-ir"; + if (devConf.Properties == null) + { + Debug.Console(0, "[{0}] WARNING: Device config does not include properties. IR will not function.", devConf.Key); + return new IrOutputPortController(irControllerKey, null, ""); + } + + var control = devConf.Properties["control"]; + if (control == null) + { + var c = new IrOutputPortController(irControllerKey, null, ""); + Debug.Console(0, c, "WARNING: Device config does not include control properties. IR will not function"); + return c; + } + + var portDevKey = control.Value("controlPortDevKey"); + var portNum = control.Value("controlPortNumber"); + IIROutputPorts irDev = null; + + if (portDevKey == null) + { + var c = new IrOutputPortController(irControllerKey, null, ""); + Debug.Console(0, c, "WARNING: control properties is missing ir device"); + return c; + } + + if (portNum == 0) + { + var c = new IrOutputPortController(irControllerKey, null, ""); + Debug.Console(0, c, "WARNING: control properties is missing ir port number"); + return c; + } + + if (portDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase) + || portDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase)) + irDev = Global.ControlSystem; + else + irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts; + + if (irDev == null) + { + var c = new IrOutputPortController(irControllerKey, null, ""); + Debug.Console(0, c, "WARNING: device with IR ports '{0}' not found", portDevKey); + return c; + } + + if (portNum <= irDev.NumberOfIROutputPorts) // success! + return new IrOutputPortController(irControllerKey, irDev.IROutputPorts[portNum], + IrDriverPathPrefix + control["irFile"].Value()); + else + { + var c = new IrOutputPortController(irControllerKey, null, ""); + Debug.Console(0, c, "WARNING: device '{0}' IR port {1} out of range", + portDevKey, portNum); + return c; + } + } + } + + /// + /// Wrapper to help in IR port creation + /// + public class IrOutPortConfig + { + public IROutputPort Port { get; set; } + public string FileName { get; set; } + + public IrOutPortConfig() + { + FileName = ""; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs new file mode 100644 index 00000000..5e71b689 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core.Config +{ + /// + /// Override this and splice on specific room type behavior, as well as other properties + /// + public class BasicConfig + { + [JsonProperty("info")] + public InfoConfig Info { get; set; } + + [JsonProperty("devices")] + public List Devices { get; set; } + + [JsonProperty("sourceLists")] + public Dictionary> SourceLists { get; set; } + + [JsonProperty("tieLines")] + public List TieLines { get; set; } + + /// + /// Checks SourceLists for a given list and returns it if found. Otherwise, returns null + /// + public Dictionary GetSourceListForKey(string key) + { + if (string.IsNullOrEmpty(key) || !SourceLists.ContainsKey(key)) + return null; + + return SourceLists[key]; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/ConfigPropertiesHelpers.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/ConfigPropertiesHelpers.cs new file mode 100644 index 00000000..2992a385 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/ConfigPropertiesHelpers.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Essentials.Core; +using PepperDash.Core; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Essentials.Core.Config +{ + public class ConfigPropertiesHelpers + { + /// + /// Returns the value of properties.hasAudio, or false if not defined + /// + public static bool GetHasAudio(DeviceConfig deviceConfig) + { + return deviceConfig.Properties.Value("hasAudio"); + } + + /// + /// Returns the value of properties.hasControls, or false if not defined + /// + public static bool GetHasControls(DeviceConfig deviceConfig) + { + return deviceConfig.Properties.Value("hasControls"); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/DeviceConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/DeviceConfig.cs new file mode 100644 index 00000000..bb95da01 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/DeviceConfig.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharpPro; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core.Config +{ + public class DeviceConfig + { + [JsonProperty("key")] + public string Key { get; set; } + + [JsonProperty("uid")] + public int Uid { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("group")] + public string Group { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("properties")] + [JsonConverter(typeof(DevicePropertiesConverter))] + public JToken Properties { get; set; } + } + + /// + /// + /// + public class DevicePropertiesConverter : JsonConverter + { + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(JToken); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return JToken.ReadFrom(reader); + } + + public override bool CanWrite + { + get + { + return false; + } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException("SOD OFF HOSER"); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs new file mode 100644 index 00000000..0a288944 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs @@ -0,0 +1,176 @@ +using System; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Core.Config; + +namespace PepperDash.Essentials.Core.Config +{ + /// + /// Loads the ConfigObject from the file + /// + public class ConfigReader + { + public static EssentialsConfig ConfigObject { get; private set; } + + public static bool LoadConfig2() + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading unmerged system/template portal configuration file."); + try + { + // Check for local config file first + var filePath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder + Global.DirectorySeparator + Global.ConfigFileName; + + bool localConfigFound = false; + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load Local config file: '{0}'", filePath); + + // Check for local config directory first + + var configFiles = GetConfigFiles(filePath); + + if (configFiles != null) + { + if (configFiles.Length > 1) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, + "****Error: Multiple Local Configuration files present. Please ensure only a single file exists and reset program.****"); + return false; + } + else if(configFiles.Length == 1) + { + localConfigFound = true; + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Found Local config file: '{0}'", filePath); + } + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, + "Local Configuration file not present.", filePath); + + } + + // Check for Portal Config + if(!localConfigFound) + { + filePath = Global.FilePathPrefix + Global.ConfigFileName; + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load Portal config file: '{0}'", filePath); + + configFiles = GetConfigFiles(filePath); + + if (configFiles != null) + { + if (configFiles.Length > 1) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, + "****Error: Multiple Portal Configuration files present. Please ensure only a single file exists and reset program.****"); + return false; + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Found Portal config file: '{0}'", filePath); + } + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Error, + "ERROR: Portal Configuration file not present. Please load file and reset program."); + return false; + } + } + + // Get the actual file path + filePath = configFiles[0].FullName; + + // Read the file + using (StreamReader fs = new StreamReader(filePath)) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading config file: '{0}'", filePath); + + if (localConfigFound) + { + ConfigObject = JObject.Parse(fs.ReadToEnd()).ToObject(); + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Local Config"); + + return true; + } + else + { + var doubleObj = JObject.Parse(fs.ReadToEnd()); + ConfigObject = PortalConfigReader.MergeConfigs(doubleObj).ToObject(); + + // Extract SystemUrl and TemplateUrl into final config output + + if (doubleObj["system_url"] != null) + { + ConfigObject.SystemUrl = doubleObj["system_url"].Value(); + } + + if (doubleObj["template_url"] != null) + { + ConfigObject.TemplateUrl = doubleObj["template_url"].Value(); + } + } + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Merged Config"); + + return true; + } + } + catch (Exception e) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "ERROR: Config load failed: \r{0}", e); + return false; + } + } + + /// + /// Returns all the files from the directory specified. + /// + /// + /// + public static FileInfo[] GetConfigFiles(string filePath) + { + // Get the directory + var dir = Path.GetDirectoryName(filePath); + + if (Directory.Exists(dir)) + { + Debug.Console(1, "Searching in Directory '{0}'", dir); + // Get the directory info + var dirInfo = new DirectoryInfo(dir); + + // Get the file name + var fileName = Path.GetFileName(filePath); + Debug.Console(1, "For Config Files matching: '{0}'", fileName); + + // Get the files that match from the directory + return dirInfo.GetFiles(fileName); + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, + "Directory not found: ", dir); + + return null; + } + } + + /// + /// Returns the group for a given device key in config + /// + /// + /// + public static string GetGroupForDeviceKey(string key) + { + var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); + return dev == null ? null : dev.Group; + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigUpdater.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigUpdater.cs new file mode 100644 index 00000000..60daaf18 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigUpdater.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Crestron.SimplSharp.Net.Http; +using Crestron.SimplSharpPro.Diagnostics; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.Config +{ + public static class ConfigUpdater + { + public static event EventHandler ConfigStatusChanged; + + public static void GetConfigFromServer(string url) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to get new config from '{0}'", url); + + // HTTP GET + var req = new HttpClientRequest(); + + try + { + req.RequestType = RequestType.Get; + req.Url.Parse(url); + + new HttpClient().DispatchAsync(req, (r, e) => + { + if (e == HTTP_CALLBACK_ERROR.COMPLETED) + { + if (r.Code == 200) + { + var newConfig = r.ContentString; + + OnStatusUpdate(eUpdateStatus.ConfigFileReceived); + + ArchiveExistingPortalConfigs(); + + CheckForLocalConfigAndDelete(); + + WriteConfigToFile(newConfig); + + RestartProgram(); + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "Config Update Process Stopped. Failed to get config file from server: {0}", r.Code); + OnStatusUpdate(eUpdateStatus.UpdateFailed); + } + } + else + Debug.Console(0, Debug.ErrorLogLevel.Error, "Request for config from Server Failed: {0}", e); + }); + } + catch (Exception e) + { + Debug.Console(1, "Error Getting Config from Server: {0}", e); + } + + } + + static void OnStatusUpdate(eUpdateStatus status) + { + var handler = ConfigStatusChanged; + + if(handler != null) + { + handler(typeof(ConfigUpdater), new ConfigStatusEventArgs(status)); + } + } + + static void WriteConfigToFile(string configData) + { + var filePath = Global.FilePathPrefix+ "configurationFile-updated.json"; + + try + { + var config = JObject.Parse(configData).ToObject(); + + ConfigWriter.WriteFile(filePath, configData); + + OnStatusUpdate(eUpdateStatus.WritingConfigFile); + } + catch (Exception e) + { + Debug.Console(1, "Error parsing new config: {0}", e); + + OnStatusUpdate(eUpdateStatus.UpdateFailed); + } + } + + /// + /// Checks for any existing portal config files and archives them + /// + static void ArchiveExistingPortalConfigs() + { + var filePath = Global.FilePathPrefix + Global.ConfigFileName; + + var configFiles = ConfigReader.GetConfigFiles(filePath); + + if (configFiles != null) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Existing config files found. Moving to Archive folder."); + + OnStatusUpdate(eUpdateStatus.ArchivingConfigs); + + MoveFilesToArchiveFolder(configFiles); + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "No Existing config files found in '{0}'. Nothing to archive", filePath); + } + } + + /// + /// Checks for presence of archive folder and if found deletes contents. + /// Moves any config files to the archive folder and adds a .bak suffix + /// + /// + static void MoveFilesToArchiveFolder(FileInfo[] files) + { + string archiveDirectoryPath = Global.FilePathPrefix + "archive"; + + if (!Directory.Exists(archiveDirectoryPath)) + { + // Directory does not exist, create it + Directory.Create(archiveDirectoryPath); + } + else + { + // Directory exists, first clear any contents + var archivedConfigFiles = ConfigReader.GetConfigFiles(archiveDirectoryPath + Global.DirectorySeparator + Global.ConfigFileName + ".bak"); + + if(archivedConfigFiles != null || archivedConfigFiles.Length > 0) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "{0} Existing files found in archive folder. Deleting.", archivedConfigFiles.Length); + + for (int i = 0; i < archivedConfigFiles.Length; i++ ) + { + var file = archivedConfigFiles[i]; + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Deleting archived file: '{0}'", file.FullName); + file.Delete(); + } + } + + } + + // Move any files from the program folder to the archive folder + foreach (var file in files) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Moving config file '{0}' to archive folder", file.FullName); + + // Moves the file and appends the .bak extension + var fileDest = archiveDirectoryPath + "/" + file.Name + ".bak"; + if(!File.Exists(fileDest)) + { + file.MoveTo(fileDest); + } + else + Debug.Console(0, Debug.ErrorLogLevel.Warning, "Cannot move file to archive folder. Existing file already exists with same name: '{0}'", fileDest); + } + } + + /// + /// Checks for LocalConfig folder in file system and deletes if found + /// + static void CheckForLocalConfigAndDelete() + { + var folderPath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder; + + if (Directory.Exists(folderPath)) + { + OnStatusUpdate(eUpdateStatus.DeletingLocalConfig); + Directory.Delete(folderPath); + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Local Config Found in '{0}'. Deleting.", folderPath); + } + } + + /// + /// Connects to the processor via SSH and restarts the program + /// + static void RestartProgram() + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to Reset Program"); + + OnStatusUpdate(eUpdateStatus.RestartingProgram); + + string response = string.Empty; + + CrestronConsole.SendControlSystemCommand(string.Format("progreset -p:{0}", InitialParametersClass.ApplicationNumber), ref response); + + Debug.Console(1, "Console Response: {0}", response); + } + + } + + public enum eUpdateStatus + { + UpdateStarted, + ConfigFileReceived, + ArchivingConfigs, + DeletingLocalConfig, + WritingConfigFile, + RestartingProgram, + UpdateSucceeded, + UpdateFailed + } + + public class ConfigStatusEventArgs : EventArgs + { + public eUpdateStatus UpdateStatus { get; private set; } + + public ConfigStatusEventArgs(eUpdateStatus status) + { + UpdateStatus = status; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigWriter.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigWriter.cs new file mode 100644 index 00000000..1cd7b93e --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigWriter.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.Config +{ + /// + /// Responsible for updating config at runtime, and writing the updates out to a local file + /// + public class ConfigWriter + { + public const string LocalConfigFolder = "LocalConfig"; + + public const long WriteTimeout = 30000; + + public static CTimer WriteTimer; + + /// + /// Updates the config properties of a device + /// + /// + /// + /// + public static bool UpdateDeviceProperties(string deviceKey, JToken properties) + { + bool success = false; + + // Get the current device config + var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(deviceKey)); + + if (deviceConfig != null) + { + // Replace the current properties JToken with the new one passed into this method + deviceConfig.Properties = properties; + + Debug.Console(1, "Updated properties of device: '{0}'", deviceKey); + + success = true; + } + + ResetTimer(); + + return success; + } + + public static bool UpdateDeviceConfig(DeviceConfig config) + { + bool success = false; + + var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(config.Key)); + + if (deviceConfig != null) + { + deviceConfig = config; + + Debug.Console(1, "Updated config of device: '{0}'", config.Key); + + success = true; + } + + ResetTimer(); + + return success; + } + + public static bool UpdateRoomConfig(DeviceConfig config) + { + bool success = false; + + var deviceConfig = ConfigReader.ConfigObject.Rooms.FirstOrDefault(d => d.Key.Equals(config.Key)); + + if (deviceConfig != null) + { + deviceConfig = config; + + Debug.Console(1, "Updated config of device: '{0}'", config.Key); + + success = true; + } + + ResetTimer(); + + return success; + } + + /// + /// Resets (or starts) the write timer + /// + static void ResetTimer() + { + if (WriteTimer == null) + WriteTimer = new CTimer(WriteConfigFile, WriteTimeout); + + WriteTimer.Reset(WriteTimeout); + + Debug.Console(1, "Config File write timer has been reset."); + } + + /// + /// Writes the current config to a file in the LocalConfig subfolder + /// + /// + private static void WriteConfigFile(object o) + { + var filePath = Global.FilePathPrefix + LocalConfigFolder + Global.DirectorySeparator + "configurationFile.json"; + + var configData = JsonConvert.SerializeObject(ConfigReader.ConfigObject); + + WriteFile(filePath, configData); + } + + /// + /// Writes + /// + /// + /// + public static void WriteFile(string filePath, string configData) + { + if (WriteTimer != null) + WriteTimer.Stop(); + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Writing Configuration to file"); + + var fileLock = new CCriticalSection(); + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to write config file: '{0}'", filePath); + + try + { + if (fileLock.TryEnter()) + { + using (StreamWriter sw = new StreamWriter(filePath)) + { + sw.Write(configData); + sw.Flush(); + } + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to enter FileLock"); + } + } + catch (Exception e) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "Error: Config write failed: \r{0}", e); + } + finally + { + if (fileLock != null && !fileLock.Disposed) + fileLock.Leave(); + + } + } + + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/EssentialsConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/EssentialsConfig.cs new file mode 100644 index 00000000..69f162d0 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/EssentialsConfig.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.Config +{ + /// + /// Loads the ConfigObject from the file + /// + public class EssentialsConfig : BasicConfig + { + [JsonProperty("system_url")] + public string SystemUrl { get; set; } + + [JsonProperty("template_url")] + public string TemplateUrl { get; set; } + + + //public CotijaConfig Cotija { get; private set; } + + [JsonProperty("systemUuid")] + public string SystemUuid + { + get + { + if (string.IsNullOrEmpty(SystemUrl)) + return "missing url"; + + var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*"); + string uuid = result.Groups[1].Value; + return uuid; + } + } + + [JsonProperty("templateUuid")] + public string TemplateUuid + { + get + { + if (string.IsNullOrEmpty(TemplateUrl)) + return "missing template url"; + + var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*"); + string uuid = result.Groups[1].Value; + return uuid; + } + } + + [JsonProperty("rooms")] + public List Rooms { get; set; } + } + + /// + /// + /// + public class SystemTemplateConfigs + { + public EssentialsConfig System { get; set; } + + public EssentialsConfig Template { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/InfoConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/InfoConfig.cs new file mode 100644 index 00000000..d5cf16f1 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/InfoConfig.cs @@ -0,0 +1,96 @@ +using System; + +using Crestron.SimplSharp; +using Crestron.SimplSharp.Reflection; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core.Config +{ + /// + /// Represents the info section of a Config file + /// + public class InfoConfig + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("date")] + public DateTime Date { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("version")] + public string Version { get; set; } + + [JsonProperty("runtimeInfo")] + public RuntimeInfo RuntimeInfo { get; set; } + + [JsonProperty("comment")] + public string Comment { get; set; } + + [JsonProperty("hostname")] + public string HostName { get; set; } + + [JsonProperty("appNumber")] + public uint AppNumber { get; set; } + + public InfoConfig() + { + Name = ""; + Date = DateTime.Now; + Type = ""; + Version = ""; + Comment = ""; + HostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); + AppNumber = InitialParametersClass.ApplicationNumber; + + RuntimeInfo = new RuntimeInfo(); + } + } + + + /// + /// Represents runtime information about the program/processor + /// + public class RuntimeInfo + { + /// + /// The name of the running application + /// + [JsonProperty("appName")] + public string AppName {get; set;} + //{ + // get + // { + // return Assembly.GetExecutingAssembly().GetName().Name; + // } + //} + + /// + /// The Assembly version of the running application + /// + [JsonProperty("assemblyVersion")] + public string AssemblyVersion {get; set;} + //{ + // get + // { + // var version = Assembly.GetExecutingAssembly().GetName().Version; + // return string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); + // } + //} + + /// + /// The OS Version of the processor (Firmware Version) + /// + [JsonProperty("osVersion")] + public string OsVersion {get; set;} + //{ + // get + // { + // return Crestron.SimplSharp.CrestronEnvironment.OSVersion.Firmware; + // } + //} + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/SourceDevicePropertiesConfigBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/SourceDevicePropertiesConfigBase.cs new file mode 100644 index 00000000..e6d13339 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/SourceDevicePropertiesConfigBase.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core.Config +{ + public class SourceDevicePropertiesConfigBase + { + public bool DisableSharing { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Constants/CommonCues.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Constants/CommonCues.cs new file mode 100644 index 00000000..fceb9ad7 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Constants/CommonCues.cs @@ -0,0 +1,127 @@ +using Crestron.SimplSharpPro; + +namespace PepperDash.Essentials.Core +{ + public static class CommonBoolCue + { + public static readonly Cue Power = new Cue("Power", 101, eCueType.Bool); + public static readonly Cue PowerOn = new Cue("PowerOn", 102, eCueType.Bool); + public static readonly Cue PowerOff = new Cue("PowerOff", 103, eCueType.Bool); + + public static readonly Cue HasPowerFeedback = new Cue("HasPowerFeedback", 101, eCueType.Bool); + public static readonly Cue PowerOnFeedback = new Cue("PowerOnFeedback", 102, eCueType.Bool); + public static readonly Cue IsOnlineFeedback = new Cue("IsOnlineFeedback", 104, eCueType.Bool); + public static readonly Cue IsWarmingUp = new Cue("IsWarmingUp", 105, eCueType.Bool); + public static readonly Cue IsCoolingDown = new Cue("IsCoolingDown", 106, eCueType.Bool); + + public static readonly Cue Dash = new Cue("Dash", 109, eCueType.Bool); + public static readonly Cue Digit0 = new Cue("Digit0", 110, eCueType.Bool); + public static readonly Cue Digit1 = new Cue("Digit1", 111, eCueType.Bool); + public static readonly Cue Digit2 = new Cue("Digit2", 112, eCueType.Bool); + public static readonly Cue Digit3 = new Cue("Digit3", 113, eCueType.Bool); + public static readonly Cue Digit4 = new Cue("Digit4", 114, eCueType.Bool); + public static readonly Cue Digit5 = new Cue("Digit5", 115, eCueType.Bool); + public static readonly Cue Digit6 = new Cue("Digit6", 116, eCueType.Bool); + public static readonly Cue Digit7 = new Cue("Digit7", 117, eCueType.Bool); + public static readonly Cue Digit8 = new Cue("Digit8", 118, eCueType.Bool); + public static readonly Cue Digit9 = new Cue("Digit9", 119, eCueType.Bool); + public static readonly Cue KeypadMisc1 = new Cue("KeypadMisc1", 120, eCueType.Bool); + public static readonly Cue KeypadMisc2 = new Cue("KeypadMisc2", 121, eCueType.Bool); + + public static readonly Cue NumericEnter = new Cue("Enter", 122, eCueType.Bool); + public static readonly Cue ChannelUp = new Cue("ChannelUp", 123, eCueType.Bool); + public static readonly Cue ChannelDown = new Cue("ChannelDown", 124, eCueType.Bool); + public static readonly Cue Last = new Cue("Last", 125, eCueType.Bool); + public static readonly Cue OpenClose = new Cue("OpenClose", 126, eCueType.Bool); + public static readonly Cue Subtitle = new Cue("Subtitle", 127, eCueType.Bool); + public static readonly Cue Audio = new Cue("Audio", 128, eCueType.Bool); + public static readonly Cue Info = new Cue("Info", 129, eCueType.Bool); + public static readonly Cue Menu = new Cue("Menu", 130, eCueType.Bool); + public static readonly Cue DeviceMenu = new Cue("DeviceMenu", 131, eCueType.Bool); + public static readonly Cue Return = new Cue("Return", 132, eCueType.Bool); + public static readonly Cue Back = new Cue("Back", 133, eCueType.Bool); + public static readonly Cue Exit = new Cue("Exit", 134, eCueType.Bool); + public static readonly Cue Clear = new Cue("Clear", 135, eCueType.Bool); + public static readonly Cue List = new Cue("List", 136, eCueType.Bool); + public static readonly Cue Guide = new Cue("Guide", 137, eCueType.Bool); + public static readonly Cue Am = new Cue("Am", 136, eCueType.Bool); + public static readonly Cue Fm = new Cue("Fm", 137, eCueType.Bool); + public static readonly Cue Up = new Cue("Up", 138, eCueType.Bool); + public static readonly Cue Down = new Cue("Down", 139, eCueType.Bool); + public static readonly Cue Left = new Cue("Left", 140, eCueType.Bool); + public static readonly Cue Right = new Cue("Right", 141, eCueType.Bool); + public static readonly Cue Select = new Cue("Select", 142, eCueType.Bool); + public static readonly Cue SmartApps = new Cue("SmartApps", 143, eCueType.Bool); + public static readonly Cue Dvr = new Cue("Dvr", 144, eCueType.Bool); + + public static readonly Cue Play = new Cue("Play", 145, eCueType.Bool); + public static readonly Cue Pause = new Cue("Pause", 146, eCueType.Bool); + public static readonly Cue Stop = new Cue("Stop", 147, eCueType.Bool); + public static readonly Cue ChapNext = new Cue("ChapNext", 148, eCueType.Bool); + public static readonly Cue ChapPrevious = new Cue("ChapPrevious", 149, eCueType.Bool); + public static readonly Cue Rewind = new Cue("Rewind", 150, eCueType.Bool); + public static readonly Cue Ffwd = new Cue("Ffwd", 151, eCueType.Bool); + public static readonly Cue Replay = new Cue("Replay", 152, eCueType.Bool); + public static readonly Cue Advance = new Cue("Advance", 153, eCueType.Bool); + public static readonly Cue Record = new Cue("Record", 154, eCueType.Bool); + public static readonly Cue Red = new Cue("Red", 155, eCueType.Bool); + public static readonly Cue Green = new Cue("Green", 156, eCueType.Bool); + public static readonly Cue Yellow = new Cue("Yellow", 157, eCueType.Bool); + public static readonly Cue Blue = new Cue("Blue", 158, eCueType.Bool); + public static readonly Cue Home = new Cue("Home", 159, eCueType.Bool); + public static readonly Cue PopUp = new Cue("PopUp", 160, eCueType.Bool); + public static readonly Cue PageUp = new Cue("PageUp", 161, eCueType.Bool); + public static readonly Cue PageDown = new Cue("PageDown", 162, eCueType.Bool); + public static readonly Cue Search = new Cue("Search", 163, eCueType.Bool); + public static readonly Cue Setup = new Cue("Setup", 164, eCueType.Bool); + public static readonly Cue RStep = new Cue("RStep", 165, eCueType.Bool); + public static readonly Cue FStep = new Cue("FStep", 166, eCueType.Bool); + + public static readonly Cue IsConnected = new Cue("IsConnected", 281, eCueType.Bool); + public static readonly Cue IsOk = new Cue("IsOk", 282, eCueType.Bool); + public static readonly Cue InWarning = new Cue("InWarning", 283, eCueType.Bool); + public static readonly Cue InError = new Cue("InError", 284, eCueType.Bool); + public static readonly Cue StatusUnknown = new Cue("StatusUnknown", 285, eCueType.Bool); + + public static readonly Cue VolumeUp = new Cue("VolumeUp", 401, eCueType.Bool); + public static readonly Cue VolumeDown = new Cue("VolumeDown", 402, eCueType.Bool); + public static readonly Cue MuteOn = new Cue("MuteOn", 403, eCueType.Bool); + public static readonly Cue MuteOff = new Cue("MuteOff", 404, eCueType.Bool); + public static readonly Cue MuteToggle = new Cue("MuteToggle", 405, eCueType.Bool); + public static readonly Cue ShowVolumeButtons = new Cue("ShowVolumeButtons", 406, eCueType.Bool); + public static readonly Cue ShowVolumeSlider = new Cue("ShowVolumeSlider", 407, eCueType.Bool); + + public static readonly Cue Hdmi1 = new Cue("Hdmi1", 451, eCueType.Bool); + public static readonly Cue Hdmi2 = new Cue("Hdmi2", 452, eCueType.Bool); + public static readonly Cue Hdmi3 = new Cue("Hdmi3", 453, eCueType.Bool); + public static readonly Cue Hdmi4 = new Cue("Hdmi4", 454, eCueType.Bool); + public static readonly Cue Hdmi5 = new Cue("Hdmi5", 455, eCueType.Bool); + public static readonly Cue Hdmi6 = new Cue("Hdmi6", 456, eCueType.Bool); + public static readonly Cue DisplayPort1 = new Cue("DisplayPort1", 457, eCueType.Bool); + public static readonly Cue DisplayPort2 = new Cue("DisplayPort2", 458, eCueType.Bool); + public static readonly Cue Dvi1 = new Cue("Dvi1", 459, eCueType.Bool); + public static readonly Cue Dvi2 = new Cue("Dvi2", 460, eCueType.Bool); + public static readonly Cue Video1 = new Cue("Video1", 461, eCueType.Bool); + public static readonly Cue Video2 = new Cue("Video2", 462, eCueType.Bool); + public static readonly Cue Component1 = new Cue("Component1", 463, eCueType.Bool); + public static readonly Cue Component2 = new Cue("Component2", 464, eCueType.Bool); + public static readonly Cue Vga1 = new Cue("Vga1", 465, eCueType.Bool); + public static readonly Cue Vga2 = new Cue("Vga2", 466, eCueType.Bool); + public static readonly Cue Rgb1 = new Cue("Rgb1", 467, eCueType.Bool); + public static readonly Cue Rgb2 = new Cue("Rgb2", 468, eCueType.Bool); + public static readonly Cue Antenna = new Cue("Antenna", 469, eCueType.Bool); + + public static readonly Cue InCall = new Cue("InCall", 501, eCueType.Bool); + } + + public static class CommonIntCue + { + public static readonly Cue MainVolumeLevel = new Cue("MainVolumeLevel", 401, eCueType.Int); + public static readonly Cue MainVolumeLevelFeedback = new Cue("MainVolumeLevelFeedback", 401, eCueType.Int); + } + + public static class CommonStringCue + { + public static readonly Cue IpConnectionsText = new Cue("IpConnectionsText", 9999, eCueType.String); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/IOPortConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/IOPortConfig.cs new file mode 100644 index 00000000..09061ff2 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/IOPortConfig.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core.CrestronIO +{ + public class IOPortConfig + { + [JsonProperty("portDeviceKey")] + public string PortDeviceKey { get; set; } + [JsonProperty("portNumber")] + public uint PortNumber { get; set; } + [JsonProperty("disablePullUpResistor")] + public bool DisablePullUpResistor { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/GenericDigitalInputDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/GenericDigitalInputDevice.cs new file mode 100644 index 00000000..3c1849f9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/GenericDigitalInputDevice.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.CrestronIO +{ + public class GenericDigitalInputDevice : Device, IDigitalInput + { + public DigitalInput InputPort { get; private set; } + + public BoolFeedback InputStateFeedback { get; private set; } + + Func InputStateFeedbackFunc + { + get + { + return () => InputPort.State; + } + } + + public GenericDigitalInputDevice(string key, DigitalInput inputPort): + base(key) + { + InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc); + + InputPort = inputPort; + + InputPort.StateChange += new DigitalInputEventHandler(InputPort_StateChange); + + } + + void InputPort_StateChange(DigitalInput digitalInput, DigitalInputEventArgs args) + { + InputStateFeedback.FireUpdate(); + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/GenericVersiportInputDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/GenericVersiportInputDevice.cs new file mode 100644 index 00000000..4c5359b9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/GenericVersiportInputDevice.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.CrestronIO +{ + /// + /// Represents a generic digital input deviced tied to a versiport + /// + public class GenericVersiportDigitalInputDevice : Device, IDigitalInput + { + public Versiport InputPort { get; private set; } + + public BoolFeedback InputStateFeedback { get; private set; } + + Func InputStateFeedbackFunc + { + get + { + return () => InputPort.DigitalIn; + } + } + + public GenericVersiportDigitalInputDevice(string key, Versiport inputPort, IOPortConfig props): + base(key) + { + InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc); + InputPort = inputPort; + InputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalInput); + if (props.DisablePullUpResistor) + InputPort.DisablePullUpResistor = true; + InputPort.VersiportChange += new VersiportEventHandler(InputPort_VersiportChange); + + Debug.Console(1, this, "Created GenericVersiportDigitalInputDevice on port '{0}'. DisablePullUpResistor: '{1}'", props.PortNumber, InputPort.DisablePullUpResistor); + } + + void InputPort_VersiportChange(Versiport port, VersiportEventArgs args) + { + Debug.Console(1, this, "Versiport change: {0}", args.Event); + + if(args.Event == eVersiportEvent.DigitalInChange) + InputStateFeedback.FireUpdate(); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/IDigitalInput.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/IDigitalInput.cs new file mode 100644 index 00000000..7c63b92c --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/IDigitalInput.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core.CrestronIO +{ + /// + /// Represents a device that provides digital input + /// + public interface IDigitalInput + { + BoolFeedback InputStateFeedback { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/GenericRelayDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/GenericRelayDevice.cs new file mode 100644 index 00000000..4786cb2b --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/GenericRelayDevice.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.CrestronIO +{ + /// + /// Represents a generic device controlled by relays + /// + public class GenericRelayDevice : Device, ISwitchedOutput + { + public Relay RelayOutput { get; private set; } + + public BoolFeedback OutputIsOnFeedback { get; private set; } + + public GenericRelayDevice(string key, Relay relay): + base(key) + { + OutputIsOnFeedback = new BoolFeedback(new Func(() => RelayOutput.State)); + + RelayOutput = relay; + RelayOutput.Register(); + + RelayOutput.StateChange += new RelayEventHandler(RelayOutput_StateChange); + } + + void RelayOutput_StateChange(Relay relay, RelayEventArgs args) + { + OutputIsOnFeedback.FireUpdate(); + } + + public void OpenRelay() + { + RelayOutput.State = false; + } + + public void CloseRelay() + { + RelayOutput.State = true; + } + + public void ToggleRelayState() + { + if (RelayOutput.State == true) + OpenRelay(); + else + CloseRelay(); + } + + #region ISwitchedOutput Members + + void ISwitchedOutput.On() + { + CloseRelay(); + } + + void ISwitchedOutput.Off() + { + OpenRelay(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/ISwitchedOutput.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/ISwitchedOutput.cs new file mode 100644 index 00000000..19f8e0df --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Relay/ISwitchedOutput.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core.CrestronIO +{ + /// + /// Describes an output capable of switching on and off + /// + public interface ISwitchedOutput + { + BoolFeedback OutputIsOnFeedback {get;} + + void On(); + void Off(); + } + + public interface ISwitchedOutputCollection + { + Dictionary SwitchedOutputs { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs new file mode 100644 index 00000000..08223e03 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// A bridge class to cover the basic features of GenericBase hardware + /// + public class CrestronGenericBaseDevice : Device, IOnline, IHasFeedback, ICommunicationMonitor, IUsageTracking + { + public virtual GenericBase Hardware { get; protected set; } + + /// + /// Returns a list containing the Outputs that we want to expose. + /// + public FeedbackCollection Feedbacks { get; private set; } + + public BoolFeedback IsOnline { get; private set; } + public BoolFeedback IsRegistered { get; private set; } + public StringFeedback IpConnectionsText { get; private set; } + + /// + /// Used by implementing classes to prevent registration with Crestron TLDM. For + /// devices like RMCs and TXs attached to a chassis. + /// + public bool PreventRegistration { get; protected set; } + + public CrestronGenericBaseDevice(string key, string name, GenericBase hardware) + : base(key, name) + { + Feedbacks = new FeedbackCollection(); + + Hardware = hardware; + IsOnline = new BoolFeedback("IsOnlineFeedback", () => Hardware.IsOnline); + IsRegistered = new BoolFeedback("IsRegistered", () => Hardware.Registered); + IpConnectionsText = new StringFeedback("IpConnectionsText", () => + string.Join(",", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray())); + + AddToFeedbackList(IsOnline, IsRegistered, IpConnectionsText); + + CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000); + } + + /// + /// Make sure that overriding classes call this! + /// Registers the Crestron device, connects up to the base events, starts communication monitor + /// + public override bool CustomActivate() + { + Debug.Console(0, this, "Activating"); + if (!PreventRegistration) + { + //Debug.Console(1, this, " Does not require registration. Skipping"); + + var response = Hardware.RegisterWithLogging(Key); + if (response != eDeviceRegistrationUnRegistrationResponse.Success) + { + //Debug.Console(0, this, "ERROR: Cannot register Crestron device: {0}", response); + return false; + } + } + + Hardware.OnlineStatusChange += new OnlineStatusChangeEventHandler(Hardware_OnlineStatusChange); + CommunicationMonitor.Start(); + + return true; + } + + /// + /// This disconnects events and unregisters the base hardware device. + /// + /// + public override bool Deactivate() + { + CommunicationMonitor.Stop(); + Hardware.OnlineStatusChange -= Hardware_OnlineStatusChange; + + return Hardware.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success; + } + + /// + /// Adds feedback(s) to the list + /// + /// + public void AddToFeedbackList(params Feedback[] newFbs) + { + foreach (var f in newFbs) + { + if (f != null) + { + if (!Feedbacks.Contains(f)) + { + Feedbacks.Add(f); + } + } + } + } + + void Hardware_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) + { + if (args.DeviceOnLine) + { + foreach (var feedback in Feedbacks) + { + if (feedback != null) + feedback.FireUpdate(); + } + } + } + + #region IStatusMonitor Members + + public StatusMonitorBase CommunicationMonitor { get; private set; } + #endregion + + #region IUsageTracking Members + + public UsageTracking UsageTracker { get; set; } + + #endregion + } + + //*********************************************************************************** + public class CrestronGenericBaseDeviceEventIds + { + public const uint IsOnline = 1; + public const uint IpConnectionsText =2; + } + + /// + /// Adds logging to Register() failure + /// + public static class GenericBaseExtensions + { + public static eDeviceRegistrationUnRegistrationResponse RegisterWithLogging(this GenericBase device, string key) + { + var result = device.Register(); + var level = result == eDeviceRegistrationUnRegistrationResponse.Success ? + Debug.ErrorLogLevel.Notice : Debug.ErrorLogLevel.Error; + Debug.Console(0, level, "Register device result: '{0}', type '{1}', result {2}", key, device, result); + //if (result != eDeviceRegistrationUnRegistrationResponse.Success) + //{ + // Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot register device '{0}': {1}", key, result); + //} + return result; + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs.orig b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs.orig new file mode 100644 index 00000000..416cc553 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron/CrestronGenericBaseDevice.cs.orig @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// A bridge class to cover the basic features of GenericBase hardware + /// + public class CrestronGenericBaseDevice : Device, IOnline, IHasFeedback, ICommunicationMonitor, IUsageTracking + { + public virtual GenericBase Hardware { get; protected set; } + + public BoolFeedback IsOnline { get; private set; } + public BoolFeedback IsRegistered { get; private set; } + public StringFeedback IpConnectionsText { get; private set; } + + public CrestronGenericBaseDevice(string key, string name, GenericBase hardware) + : base(key, name) + { + Hardware = hardware; + IsOnline = new BoolFeedback(CommonBoolCue.IsOnlineFeedback, () => Hardware.IsOnline); + IsRegistered = new BoolFeedback(new Cue("IsRegistered", 0, eCueType.Bool), () => Hardware.Registered); + IpConnectionsText = new StringFeedback(CommonStringCue.IpConnectionsText, () => + string.Join(",", Hardware.ConnectedIpList.Select(cip => cip.DeviceIpAddress).ToArray())); + CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, hardware, 120000, 300000); + } + + /// + /// Make sure that overriding classes call this! + /// Registers the Crestron device, connects up to the base events, starts communication monitor + /// + public override bool CustomActivate() + { + Debug.Console(0, this, "Activating"); + var response = Hardware.RegisterWithLogging(Key); + if (response != eDeviceRegistrationUnRegistrationResponse.Success) + { +<<<<<<< HEAD + Debug.Console(0, this, "ERROR: Cannot register Crestron device: {0}", response); + return false; + } +======= + Debug.Console(0, this, "ERROR: Cannot register Crestron device: {0}", response); + return false; + } +>>>>>>> origin/feature/ecs-342-neil + Hardware.OnlineStatusChange += new OnlineStatusChangeEventHandler(Hardware_OnlineStatusChange); + CommunicationMonitor.Start(); + + return true; + } + + /// + /// This disconnects events and unregisters the base hardware device. + /// + /// + public override bool Deactivate() + { + CommunicationMonitor.Stop(); + Hardware.OnlineStatusChange -= Hardware_OnlineStatusChange; + + return Hardware.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success; + } + + /// + /// Returns a list containing the Outputs that we want to expose. + /// + public virtual List Feedbacks + { + get + { + return new List + { + IsOnline, + IsRegistered, + IpConnectionsText + }; + } + } + + void Hardware_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) + { + IsOnline.FireUpdate(); + } + + #region IStatusMonitor Members + + public StatusMonitorBase CommunicationMonitor { get; private set; } + #endregion + + #region IUsageTracking Members + + public UsageTracking UsageTracker { get; set; } + + #endregion + } + + //*********************************************************************************** + public class CrestronGenericBaseDeviceEventIds + { + public const uint IsOnline = 1; + public const uint IpConnectionsText =2; + } + + /// + /// Adds logging to Register() failure + /// + public static class GenericBaseExtensions + { + public static eDeviceRegistrationUnRegistrationResponse RegisterWithLogging(this GenericBase device, string key) + { + var result = device.Register(); + if (result != eDeviceRegistrationUnRegistrationResponse.Success) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot register device '{0}': {1}", key, result); + } + return result; + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Cues and DevAction/Cues.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Cues and DevAction/Cues.cs new file mode 100644 index 00000000..a3940a98 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Cues and DevAction/Cues.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + + +namespace PepperDash.Essentials.Core +{ + /// + /// Encapsulates a string-named, joined and typed command to a device + /// + [Obsolete()] + public class DevAction + { + public Cue Cue { get; private set; } + public Action Action { get; private set; } + + + public DevAction(Cue cue, Action action) + { + Cue = cue; + Action = action; + } + } + + public enum eCueType + { + Bool, Int, String, Serial, Void, Other + } + + + /// + /// The Cue class is a container to represent a name / join number / type for simplifying + /// commands coming into devices. + /// + public class Cue + { + public string Name { get; private set; } + public uint Number { get; private set; } + public eCueType Type { get; private set; } + + public Cue(string name, uint join, eCueType type) + { + Name = name; + Number = join; + Type = type; + } + + /// + /// Override that prints out the cue's data + /// + public override string ToString() + { + return string.Format("{0} Cue '{1}'-{2}", Type, Name, Number); + } + + ///// + ///// Returns a new Cue with JoinNumber offset + ///// + //public Cue GetOffsetCopy(uint offset) + //{ + // return new Cue(Name, Number + offset, Type); + //} + + /// + /// Helper method to create a Cue of Bool type + /// + /// Cue + public static Cue BoolCue(string name, uint join) + { + return new Cue(name, join, eCueType.Bool); + } + + /// + /// Helper method to create a Cue of ushort type + /// + /// Cue + public static Cue UShortCue(string name, uint join) + { + return new Cue(name, join, eCueType.Int); + } + + /// + /// Helper method to create a Cue of string type + /// + /// Cue + public static Cue StringCue(string name, uint join) + { + return new Cue(name, join, eCueType.String); + } + + public static readonly Cue DefaultBoolCue = new Cue("-none-", 0, eCueType.Bool); + public static readonly Cue DefaultIntCue = new Cue("-none-", 0, eCueType.Int); + public static readonly Cue DefaultStringCue = new Cue("-none-", 0, eCueType.String); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceControlsParentInterfaces/IPresentationSource.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceControlsParentInterfaces/IPresentationSource.cs new file mode 100644 index 00000000..ac45a9d4 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceControlsParentInterfaces/IPresentationSource.cs @@ -0,0 +1,19 @@ +//using System; +//using System.Collections.Generic; +//using Crestron.SimplSharpPro; +//using Crestron.SimplSharpPro.DeviceSupport; + +//using PepperDash.Core; + + +//namespace PepperDash.Essentials.Core +//{ +// public interface IPresentationSource : IKeyed +// { +// string Name { get; } +// PresentationSourceType Type { get; } +// string IconName { get; set; } +// BoolFeedback HasPowerOnFeedback { get; } + +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IChannel.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IChannel.cs new file mode 100644 index 00000000..5a38c703 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IChannel.cs @@ -0,0 +1,47 @@ +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public interface IChannel + { + void ChannelUp(bool pressRelease); + void ChannelDown(bool pressRelease); + void LastChannel(bool pressRelease); + void Guide(bool pressRelease); + void Info(bool pressRelease); + void Exit(bool pressRelease); + } + + /// + /// + /// + public static class IChannelExtensions + { + public static void LinkButtons(this IChannel dev, BasicTriList triList) + { + triList.SetBoolSigAction(123, dev.ChannelUp); + triList.SetBoolSigAction(124, dev.ChannelDown); + triList.SetBoolSigAction(125, dev.LastChannel); + triList.SetBoolSigAction(137, dev.Guide); + triList.SetBoolSigAction(129, dev.Info); + triList.SetBoolSigAction(134, dev.Exit); + } + + public static void UnlinkButtons(this IChannel dev, BasicTriList triList) + { + triList.ClearBoolSigAction(123); + triList.ClearBoolSigAction(124); + triList.ClearBoolSigAction(125); + triList.ClearBoolSigAction(137); + triList.ClearBoolSigAction(129); + triList.ClearBoolSigAction(134); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IColorFunctions.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IColorFunctions.cs new file mode 100644 index 00000000..232362e9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IColorFunctions.cs @@ -0,0 +1,41 @@ +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public interface IColor + { + void Red(bool pressRelease); + void Green(bool pressRelease); + void Yellow(bool pressRelease); + void Blue(bool pressRelease); + } + + /// + /// + /// + public static class IColorExtensions + { + public static void LinkButtons(this IColor dev, BasicTriList TriList) + { + TriList.SetBoolSigAction(155, dev.Red); + TriList.SetBoolSigAction(156, dev.Green); + TriList.SetBoolSigAction(157, dev.Yellow); + TriList.SetBoolSigAction(158, dev.Blue); + } + + public static void UnlinkButtons(this IColor dev, BasicTriList triList) + { + triList.ClearBoolSigAction(155); + triList.ClearBoolSigAction(156); + triList.ClearBoolSigAction(157); + triList.ClearBoolSigAction(158); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDPad.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDPad.cs new file mode 100644 index 00000000..2e0f3ae7 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDPad.cs @@ -0,0 +1,50 @@ +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public interface IDPad + { + void Up(bool pressRelease); + void Down(bool pressRelease); + void Left(bool pressRelease); + void Right(bool pressRelease); + void Select(bool pressRelease); + void Menu(bool pressRelease); + void Exit(bool pressRelease); + } + + /// + /// + /// + public static class IDPadExtensions + { + public static void LinkButtons(this IDPad dev, BasicTriList triList) + { + triList.SetBoolSigAction(138, dev.Up); + triList.SetBoolSigAction(139, dev.Down); + triList.SetBoolSigAction(140, dev.Left); + triList.SetBoolSigAction(141, dev.Right); + triList.SetBoolSigAction(142, dev.Select); + triList.SetBoolSigAction(130, dev.Menu); + triList.SetBoolSigAction(134, dev.Exit); + } + + public static void UnlinkButtons(this IDPad dev, BasicTriList triList) + { + triList.ClearBoolSigAction(138); + triList.ClearBoolSigAction(139); + triList.ClearBoolSigAction(140); + triList.ClearBoolSigAction(141); + triList.ClearBoolSigAction(142); + triList.ClearBoolSigAction(130); + triList.ClearBoolSigAction(134); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDiscPlayerControls.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDiscPlayerControls.cs new file mode 100644 index 00000000..31fb83b4 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDiscPlayerControls.cs @@ -0,0 +1,13 @@ +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; + +namespace PepperDash.Essentials.Core +{ + + public interface IDiscPlayerControls : IColor, IDPad, INumericKeypad, IPower, ITransport, IUiDisplayInfo + { + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDisplayBasic.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDisplayBasic.cs new file mode 100644 index 00000000..5624f77c --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDisplayBasic.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core.Devices.DeviceTypeInterfaces +{ + public interface IDisplayBasic + { + void InputHdmi1(); + void InputHdmi2(); + void InputHdmi3(); + void InputHdmi4(); + void InputDisplayPort1(); + void InputDvi1(); + void InputVideo1(); + void InputVga1(); + void InputVga2(); + void InputRgb1(); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDumbSource.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDumbSource.cs new file mode 100644 index 00000000..1d4829cd --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDumbSource.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + public interface IDumbSource + { + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDvr.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDvr.cs new file mode 100644 index 00000000..9e55702c --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IDvr.cs @@ -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.DeviceSupport; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public interface IDvr : IDPad + { + void DvrList(bool pressRelease); + void Record(bool pressRelease); + } + + /// + /// + /// + public static class IDvrExtensions + { + public static void LinkButtons(this IDvr dev, BasicTriList triList) + { + triList.SetBoolSigAction(136, dev.DvrList); + triList.SetBoolSigAction(152, dev.Record); + } + + public static void UnlinkButtons(this IDvr dev, BasicTriList triList) + { + triList.ClearBoolSigAction(136); + triList.ClearBoolSigAction(152); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs new file mode 100644 index 00000000..0294a0b5 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs @@ -0,0 +1,84 @@ +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public interface INumericKeypad + { + void Digit0(bool pressRelease); + void Digit1(bool pressRelease); + void Digit2(bool pressRelease); + void Digit3(bool pressRelease); + void Digit4(bool pressRelease); + void Digit5(bool pressRelease); + void Digit6(bool pressRelease); + void Digit7(bool pressRelease); + void Digit8(bool pressRelease); + void Digit9(bool pressRelease); + + /// + /// Used to hide/show the button and/or text on the left-hand keypad button + /// + bool HasKeypadAccessoryButton1 { get; } + string KeypadAccessoryButton1Label { get; } + void KeypadAccessoryButton1(bool pressRelease); + + bool HasKeypadAccessoryButton2 { get; } + string KeypadAccessoryButton2Label { get; } + void KeypadAccessoryButton2(bool pressRelease); + } + + public interface ISetTopBoxNumericKeypad : INumericKeypad + { + void Dash(bool pressRelease); + void KeypadEnter(bool pressRelease); + } + + /// + /// + /// + public static class INumericExtensions + { + /// + /// Links to the smart object, and sets the misc button's labels on joins x and y + /// + public static void LinkButtons(this INumericKeypad dev, BasicTriList trilist) + { + trilist.SetBoolSigAction(110, dev.Digit0); + trilist.SetBoolSigAction(111, dev.Digit1); + trilist.SetBoolSigAction(112, dev.Digit2); + trilist.SetBoolSigAction(113, dev.Digit3); + trilist.SetBoolSigAction(114, dev.Digit4); + trilist.SetBoolSigAction(115, dev.Digit5); + trilist.SetBoolSigAction(116, dev.Digit6); + trilist.SetBoolSigAction(117, dev.Digit7); + trilist.SetBoolSigAction(118, dev.Digit8); + trilist.SetBoolSigAction(119, dev.Digit9); + trilist.SetBoolSigAction(120, dev.KeypadAccessoryButton1); + trilist.SetBoolSigAction(121, dev.KeypadAccessoryButton2); + trilist.StringInput[111].StringValue = dev.KeypadAccessoryButton1Label; + trilist.StringInput[111].StringValue = dev.KeypadAccessoryButton2Label; + } + + public static void UnlinkButtons(this INumericKeypad dev, BasicTriList trilist) + { + trilist.ClearBoolSigAction(110); + trilist.ClearBoolSigAction(111); + trilist.ClearBoolSigAction(112); + trilist.ClearBoolSigAction(113); + trilist.ClearBoolSigAction(114); + trilist.ClearBoolSigAction(115); + trilist.ClearBoolSigAction(116); + trilist.ClearBoolSigAction(117); + trilist.ClearBoolSigAction(118); + trilist.ClearBoolSigAction(119); + trilist.ClearBoolSigAction(120); + trilist.ClearBoolSigAction(121); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IPower.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IPower.cs new file mode 100644 index 00000000..a392d149 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IPower.cs @@ -0,0 +1,48 @@ +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.Fusion; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; + + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public interface IPower + { + void PowerOn(); + void PowerOff(); + void PowerToggle(); + BoolFeedback PowerIsOnFeedback { get; } + } + + /// + /// + /// + public static class IPowerExtensions + { + public static void LinkButtons(this IPower dev, BasicTriList triList) + { + triList.SetSigFalseAction(101, dev.PowerOn); + triList.SetSigFalseAction(102, dev.PowerOff); + triList.SetSigFalseAction(103, dev.PowerToggle); + dev.PowerIsOnFeedback.LinkInputSig(triList.BooleanInput[101]); + } + + public static void UnlinkButtons(this IPower dev, BasicTriList triList) + { + triList.ClearBoolSigAction(101); + triList.ClearBoolSigAction(102); + triList.ClearBoolSigAction(103); + dev.PowerIsOnFeedback.UnlinkInputSig(triList.BooleanInput[101]); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ISetTopBoxControls.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ISetTopBoxControls.cs new file mode 100644 index 00000000..8b043cac --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ISetTopBoxControls.cs @@ -0,0 +1,55 @@ +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public interface ISetTopBoxControls : IChannel, IColor, IDPad, ISetTopBoxNumericKeypad, + ITransport, IUiDisplayInfo + { + /// + /// Show DVR controls? + /// + bool HasDvr { get; } + + /// + /// Show presets controls? + /// + bool HasPresets { get; } + + /// + /// Show number pad controls? + /// + bool HasNumeric { get; } + + /// + /// Show D-pad controls? + /// + bool HasDpad { get; } + + PepperDash.Essentials.Core.Presets.DevicePresetsModel PresetsModel { get; } + void LoadPresets(string filePath); + + void DvrList(bool pressRelease); + void Replay(bool pressRelease); + } + + public static class ISetTopBoxControlsExtensions + { + public static void LinkButtons(this ISetTopBoxControls dev, BasicTriList triList) + { + triList.SetBoolSigAction(136, dev.DvrList); + triList.SetBoolSigAction(152, dev.Replay); + } + + public static void UnlinkButtons(this ISetTopBoxControls dev, BasicTriList triList) + { + triList.ClearBoolSigAction(136); + triList.ClearBoolSigAction(152); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ITransport.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ITransport.cs new file mode 100644 index 00000000..1189c10b --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ITransport.cs @@ -0,0 +1,53 @@ +using Crestron.SimplSharpPro.DeviceSupport; + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public interface ITransport + { + void Play(bool pressRelease); + void Pause(bool pressRelease); + void Rewind(bool pressRelease); + void FFwd(bool pressRelease); + void ChapMinus(bool pressRelease); + void ChapPlus(bool pressRelease); + void Stop(bool pressRelease); + void Record(bool pressRelease); + } + + /// + /// + /// + public static class ITransportExtensions + { + /// + /// Attaches to trilist joins: Play:145, Pause:146, Stop:147, ChapPlus:148, ChapMinus:149, Rewind:150, Ffwd:151, Record:154 + /// + /// + public static void LinkButtons(this ITransport dev, BasicTriList triList) + { + triList.SetBoolSigAction(145, dev.Play); + triList.SetBoolSigAction(146, dev.Pause); + triList.SetBoolSigAction(147, dev.Stop); + triList.SetBoolSigAction(148, dev.ChapPlus); + triList.SetBoolSigAction(149, dev.ChapMinus); + triList.SetBoolSigAction(150, dev.Rewind); + triList.SetBoolSigAction(151, dev.FFwd); + triList.SetBoolSigAction(154, dev.Record); + } + + public static void UnlinkButtons(this ITransport dev, BasicTriList triList) + { + triList.ClearBoolSigAction(145); + triList.ClearBoolSigAction(146); + triList.ClearBoolSigAction(147); + triList.ClearBoolSigAction(148); + triList.ClearBoolSigAction(149); + triList.ClearBoolSigAction(150); + triList.ClearBoolSigAction(151); + triList.ClearBoolSigAction(154); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IUiDisplayInfo.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IUiDisplayInfo.cs new file mode 100644 index 00000000..fb51f7e2 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IUiDisplayInfo.cs @@ -0,0 +1,12 @@ +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// Describes things needed to show on UI + /// + public interface IUiDisplayInfo : IKeyed + { + uint DisplayUiType { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IWarmingCooling.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IWarmingCooling.cs new file mode 100644 index 00000000..2baf1548 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/IWarmingCooling.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Defines a class that has warm up and cool down + /// + public interface IWarmingCooling + { + BoolFeedback IsWarmingUpFeedback { get; } + BoolFeedback IsCoolingDownFeedback { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/Template.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/Template.cs new file mode 100644 index 00000000..31f71df9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/Template.cs @@ -0,0 +1,9 @@ +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.SmartObjects; + +namespace PepperDash.Essentials.Core +{ + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/.IHasFeedbacks_BASE_3692.cs.swp b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/.IHasFeedbacks_BASE_3692.cs.swp new file mode 100644 index 00000000..827c466a Binary files /dev/null and b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/.IHasFeedbacks_BASE_3692.cs.swp differ diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/.IHasFeedbacks_LOCAL_3692.cs.swp b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/.IHasFeedbacks_LOCAL_3692.cs.swp new file mode 100644 index 00000000..e06196a4 Binary files /dev/null and b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/.IHasFeedbacks_LOCAL_3692.cs.swp differ diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/.IHasFeedbacks_REMOTE_3692.cs.swp b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/.IHasFeedbacks_REMOTE_3692.cs.swp new file mode 100644 index 00000000..842256cc Binary files /dev/null and b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/.IHasFeedbacks_REMOTE_3692.cs.swp differ diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/CodecInterfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/CodecInterfaces.cs new file mode 100644 index 00000000..afb1da17 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/CodecInterfaces.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Adds control of codec receive volume + /// + public interface IReceiveVolume + { + // Break this out into 3 interfaces + void SetReceiveVolume(ushort level); + void ReceiveMuteOn(); + void ReceiveMuteOff(); + void ReceiveMuteToggle(); + IntFeedback ReceiveLevelFeedback { get; } + BoolFeedback ReceiveMuteIsOnFeedback { get; } + } + + /// + /// Adds control of codec transmit volume + /// + public interface ITransmitVolume + { + void SetTransmitVolume(ushort level); + void TransmitMuteOn(); + void TransmitMuteOff(); + void TransmitMuteToggle(); + IntFeedback TransmitLevelFeedback { get; } + BoolFeedback TransmitMuteIsOnFeedback { get; } + } + + /// + /// Adds control of codec privacy function (microphone mute) + /// + public interface IPrivacy + { + void PrivacyModeOn(); + void PrivacyModeOff(); + void PrivacyModeToggle(); + BoolFeedback PrivacyModeIsOnFeedback { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/CodecInterfaces.cs.orig b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/CodecInterfaces.cs.orig new file mode 100644 index 00000000..04d5aac9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/CodecInterfaces.cs.orig @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Requirements for a device that has dialing capabilities + /// + public interface IHasDialer + { + // Add requirements for Dialer functionality + + void Dial(string number); +<<<<<<< HEAD + void EndCall(string number); +======= + void EndCall(object activeCall); + void EndAllCalls(); +>>>>>>> origin/feature/cisco-spark-2 + void AcceptCall(); + void RejectCall(); + void SendDtmf(string digit); + + IntFeedback ActiveCallCountFeedback { get; } + BoolFeedback IncomingCallFeedback { get; } + } + + /// + /// Defines minimum volume controls for a codec device with dialing capabilities + /// + public interface ICodecAudio : IBasicVolumeWithFeedback, IPrivacy + { + + } + + /// + /// Adds control of codec receive volume + /// + public interface IReceiveVolume + { + // Break this out into 3 interfaces + void SetReceiveVolume(ushort level); + void ReceiveMuteOn(); + void ReceiveMuteOff(); + void ReceiveMuteToggle(); + IntFeedback ReceiveLevelFeedback { get; } + BoolFeedback ReceiveMuteIsOnFeedback { get; } + } + + /// + /// Adds control of codec transmit volume + /// + public interface ITransmitVolume + { + void SetTransmitVolume(ushort level); + void TransmitMuteOn(); + void TransmitMuteOff(); + void TransmitMuteToggle(); + IntFeedback TransmitLevelFeedback { get; } + BoolFeedback TransmitMuteIsOnFeedback { get; } + } + + /// + /// Adds control of codec privacy function (microphone mute) + /// + public interface IPrivacy + { + void PrivacyModeOn(); + void PrivacyModeOff(); + void PrivacyModeToggle(); + BoolFeedback PrivacyModeIsOnFeedback { get; } + } + + public interface IHasCallHistory + { + // Add recent calls list + } + + public interface IHasDirectory + { + + } + + public interface IHasObtp + { + + // Upcoming Meeting warning event + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/CrestronProcessor.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/CrestronProcessor.cs new file mode 100644 index 00000000..1b0264eb --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/CrestronProcessor.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core.CrestronIO; + +namespace PepperDash.Essentials.Core.Devices +{ + /// + /// This wrapper class is meant to allow interfaces to be applied to any Crestron processor + /// + public class CrestronProcessor : Device, ISwitchedOutputCollection + { + public Dictionary SwitchedOutputs { get; private set; } + + public Crestron.SimplSharpPro.CrestronControlSystem Processor { get; private set; } + + public CrestronProcessor(string key) + : base(key) + { + SwitchedOutputs = new Dictionary(); + Processor = Global.ControlSystem; + + GetRelays(); + } + + /// + /// Creates a GenericRelayDevice for each relay on the processor and adds them to the SwitchedOutputs collection + /// + void GetRelays() + { + try + { + if (Processor.SupportsRelay) + { + for (uint i = 1; i <= Processor.NumberOfRelayPorts; i++) + { + var relay = new GenericRelayDevice(string.Format("{0}-relay-{1}", this.Key, i), Processor.RelayPorts[i]); + SwitchedOutputs.Add(i, relay); + } + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error Getting Relays from processor:\n '{0}'", e); + } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceApiBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceApiBase.cs new file mode 100644 index 00000000..34669ff5 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceApiBase.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core.Devices +{ + /// + /// Base class for all Device APIs + /// + public abstract class DeviceApiBase + { + public Dictionary ActionApi { get; protected set; } + public Dictionary FeedbackApi { get; protected set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs new file mode 100644 index 00000000..32d53388 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Reflection; +using Newtonsoft.Json; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + public class DeviceJsonApi + { + /// + /// + /// + /// + public static void DoDeviceActionWithJson(string json) + { + var action = JsonConvert.DeserializeObject(json); + DoDeviceAction(action); + } + + + /// + /// + /// + /// + public static void DoDeviceAction(DeviceActionWrapper action) + { + var key = action.DeviceKey; + var obj = FindObjectOnPath(key); + if (obj == null) + return; + + CType t = obj.GetType(); + var method = t.GetMethod(action.MethodName); + if (method == null) + { + Debug.Console(0, "Method '{0}' not found", action.MethodName); + return; + } + var mParams = method.GetParameters(); + // Add empty params if not provided + if (action.Params == null) action.Params = new object[0]; + if (mParams.Length > action.Params.Length) + { + Debug.Console(0, "Method '{0}' requires {1} params", action.MethodName, mParams.Length); + return; + } + object[] convertedParams = mParams + .Select((p, i) => Convert.ChangeType(action.Params[i], p.ParameterType, + System.Globalization.CultureInfo.InvariantCulture)) + .ToArray(); + object ret = method.Invoke(obj, convertedParams); + } + + /// + /// + /// + /// + /// + public static string GetProperties(string deviceObjectPath) + { + var obj = FindObjectOnPath(deviceObjectPath); + if (obj == null) + return "{ \"error\":\"No Device\"}"; + + 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); + } + + /// + /// + /// + /// + /// + public static string GetMethods(string deviceObjectPath) + { + var obj = FindObjectOnPath(deviceObjectPath); + if (obj == null) + return "{ \"error\":\"No Device\"}"; + + // 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\"}"; + + // 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); + } + + + /// + /// Walks down a dotted object path, starting with a Device, and returns the object + /// at the end of the path + /// + public static object FindObjectOnPath(string deviceObjectPath) + { + var path = deviceObjectPath.Split('.'); + + var dev = DeviceManager.GetDeviceForKey(path[0]); + if (dev == null) + { + Debug.Console(0, "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.Console(0, 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.Console(0, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr); + } + + CType oType = obj.GetType(); + var prop = oType.GetProperty(objName); + if (prop == null) + { + Debug.Console(0, 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.Console(0, 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.Console(0, " 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.Console(0, " Index Out of range"); + else if (e.InnerException is KeyNotFoundException) + Debug.Console(0, " Key not found"); + return null; + } + } + + } + else + obj = prop.GetValue(obj, null); + } + } + return obj; + } + + /// + /// Sets a property on an object. + /// + /// + /// + public static string SetProperty(string deviceObjectPath) + { + throw new NotImplementedException("This could be really useful. Finish it please"); + + //var obj = FindObjectOnPath(deviceObjectPath); + //if (obj == null) + // return "{\"error\":\"No object found\"}"; + + //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); + } + } + + public class DeviceActionWrapper + { + public string DeviceKey { get; set; } + public string MethodName { get; set; } + public object[] Params { get; set; } + } + + public class PropertyNameType + { + 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 { get + { + if (PropInfo.CanRead) + { + try + { + return PropInfo.GetValue(Parent, null).ToString(); + } + 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; + Parent = parent; + } + } + + public class MethodNameParams + { + [JsonIgnore] + public MethodInfo MethodInfo { get; private set; } + + public string Name { get { return MethodInfo.Name; } } + public IEnumerable Params { get { + return MethodInfo.GetParameters().Select(p => + new NameType { Name = p.Name, Type = p.ParameterType.Name }); + } } + + public MethodNameParams(MethodInfo info) + { + MethodInfo = info; + } + } + + public class NameType + { + public string Name { get; set; } + public string Type { get; set; } + } + + [AttributeUsage(AttributeTargets.All)] + public class ApiAttribute : CAttribute + { + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs new file mode 100644 index 00000000..6fa1f558 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs @@ -0,0 +1,268 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.EthernetCommunication; +using Crestron.SimplSharpPro.UI; +using Crestron.SimplSharp.Reflection; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + public static class DeviceManager + { + //public static List Devices { get { return _Devices; } } + //static List _Devices = new List(); + + static Dictionary Devices = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Returns a copy of all the devices in a list + /// + public static List AllDevices { get { return new List(Devices.Values); } } + + public static void Initialize(CrestronControlSystem cs) + { + 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); + } + + /// + /// Calls activate steps on all Device class items + /// + public static void ActivateAll() + { + // PreActivate all devices + foreach (var d in Devices.Values) + { + try + { + if (d is Device) + (d as Device).PreActivate(); + } + catch (Exception e) + { + Debug.Console(0, d, "ERROR: Device PreActivation failure:\r{0}", e); + } + } + + // Activate all devices + foreach (var d in Devices.Values) + { + try + { + if (d is Device) + (d as Device).Activate(); + } + catch (Exception e) + { + Debug.Console(0, d, "ERROR: Device Activation failure:\r{0}", e); + } + } + + // PostActivate all devices + foreach (var d in Devices.Values) + { + try + { + if (d is Device) + (d as Device).PostActivate(); + } + catch (Exception e) + { + Debug.Console(0, d, "ERROR: Device PostActivation failure:\r{0}", e); + } + } + } + + /// + /// Calls activate on all Device class items + /// + public static void DeactivateAll() + { + foreach (var d in Devices.Values) + { + if (d is Device) + (d as Device).Deactivate(); + } + } + + //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 ListDevices(string s) + { + Debug.Console(0, "{0} Devices registered with Device Mangager:",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.Console(0, " [{0}] {1}", d.Key, name); + } + } + + static void ListDeviceFeedbacks(string devKey) + { + var dev = GetDeviceForKey(devKey); + if(dev == null) + { + Debug.Console(0, "Device '{0}' not found", devKey); + return; + } + var statusDev = dev as IHasFeedback; + if(statusDev == null) + { + Debug.Console(0, "Device '{0}' does not have visible feedbacks", devKey); + return; + } + statusDev.DumpFeedbacksToConsole(true); + } + + //static void ListDeviceCommands(string devKey) + //{ + // var dev = GetDeviceForKey(devKey); + // if (dev == null) + // { + // Debug.Console(0, "Device '{0}' not found", devKey); + // return; + // } + // Debug.Console(0, "This needs to be reworked. Stay tuned.", devKey); + //} + + static void ListDeviceCommStatuses(string input) + { + var sb = new StringBuilder(); + foreach (var dev in Devices.Values) + { + if (dev is ICommunicationMonitor) + sb.Append(string.Format("{0}: {1}\r", dev.Key, (dev as ICommunicationMonitor).CommunicationMonitor.Status)); + } + CrestronConsole.ConsoleCommandResponse(sb.ToString()); + } + + + //static void DoDeviceCommand(string command) + //{ + // Debug.Console(0, "Not yet implemented. Stay tuned"); + //} + + public static void AddDevice(IKeyed newDev) + { + // 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(Devices.ContainsKey(newDev.Key)) + { + Debug.Console(0, newDev, "WARNING: A device with this key already exists. Not added to manager"); + return; + } + Devices.Add(newDev.Key, newDev); + //if (!(_Devices.Contains(newDev))) + // _Devices.Add(newDev); + } + + public static void RemoveDevice(IKeyed newDev) + { + if(newDev == null) + return; + if (Devices.ContainsKey(newDev.Key)) + Devices.Remove(newDev.Key); + //if (_Devices.Contains(newDev)) + // _Devices.Remove(newDev); + else + Debug.Console(0, "Device manager: Device '{0}' does not exist in manager. Cannot remove", newDev.Key); + } + + public static IEnumerable GetDeviceKeys() + { + //return _Devices.Select(d => d.Key).ToList(); + return Devices.Keys; + } + + public static IEnumerable 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]; + + return null; + } + + /// + /// Console handler that simulates com port data receive + /// + /// + public static void SimulateComReceiveOnDevice(string s) + { + // devcomsim:1 xyzabc + var match = Regex.Match(s, @"(\S*)\s*(.*)"); + if (match.Groups.Count < 3) + { + CrestronConsole.ConsoleCommandResponse(" Format: devsimreceive:P "); + return; + } + //Debug.Console(2, "**** {0} - {1} ****", match.Groups[1].Value, match.Groups[2].Value); + + ComPortController 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; + } + com.SimulateReceive(match.Groups[2].Value); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs.orig b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs.orig new file mode 100644 index 00000000..e768807b --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs.orig @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.EthernetCommunication; +using Crestron.SimplSharpPro.UI; +using Crestron.SimplSharp.Reflection; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + public static class DeviceManager + { + //public static List Devices { get { return _Devices; } } + //static List _Devices = new List(); + + static Dictionary Devices = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Returns a copy of all the devices in a list + /// + public static List AllDevices { get { return new List(Devices.Values); } } + + public static void Initialize(CrestronControlSystem cs) + { + 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); + } + + /// + /// Calls activate on all Device class items + /// + public static void ActivateAll() + { + foreach (var d in Devices.Values) +<<<<<<< HEAD + { + try + { + if (d is Device) + (d as Device).Activate(); + } + catch (Exception e) + { + Debug.Console(0, d, "ERROR: Device activation failure:\r{0}", e); + } + } +======= + { + try + { + if (d is Device) + (d as Device).Activate(); + } + catch (Exception e) + { + Debug.Console(0, d, "ERROR: Device activation failure:\r{0}", e); + } + } +>>>>>>> origin/feature/ecs-342-neil + } + + /// + /// Calls activate on all Device class items + /// + public static void DeactivateAll() + { + foreach (var d in Devices.Values) + { + if (d is Device) + (d as Device).Deactivate(); + } + } + + //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 ListDevices(string s) + { + Debug.Console(0, "{0} Devices registered with Device Mangager:",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.Console(0, " [{0}] {1}", d.Key, name); + } + } + + static void ListDeviceFeedbacks(string devKey) + { + var dev = GetDeviceForKey(devKey); + if(dev == null) + { + Debug.Console(0, "Device '{0}' not found", devKey); + return; + } + var statusDev = dev as IHasFeedback; + if(statusDev == null) + { + Debug.Console(0, "Device '{0}' does not have visible feedbacks", devKey); + return; + } + statusDev.DumpFeedbacksToConsole(true); + } + + //static void ListDeviceCommands(string devKey) + //{ + // var dev = GetDeviceForKey(devKey); + // if (dev == null) + // { + // Debug.Console(0, "Device '{0}' not found", devKey); + // return; + // } + // Debug.Console(0, "This needs to be reworked. Stay tuned.", devKey); + //} + + static void ListDeviceCommStatuses(string input) + { + var sb = new StringBuilder(); + foreach (var dev in Devices.Values) + { + if (dev is ICommunicationMonitor) + sb.Append(string.Format("{0}: {1}\r", dev.Key, (dev as ICommunicationMonitor).CommunicationMonitor.Status)); + } + CrestronConsole.ConsoleCommandResponse(sb.ToString()); + } + + + //static void DoDeviceCommand(string command) + //{ + // Debug.Console(0, "Not yet implemented. Stay tuned"); + //} + + public static void AddDevice(IKeyed newDev) + { + // 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(Devices.ContainsKey(newDev.Key)) + { + Debug.Console(0, newDev, "WARNING: A device with this key already exists. Not added to manager"); + return; + } + Devices.Add(newDev.Key, newDev); + //if (!(_Devices.Contains(newDev))) + // _Devices.Add(newDev); + } + + public static void RemoveDevice(IKeyed newDev) + { + if(newDev == null) + return; + if (Devices.ContainsKey(newDev.Key)) + Devices.Remove(newDev.Key); + //if (_Devices.Contains(newDev)) + // _Devices.Remove(newDev); + else + Debug.Console(0, "Device manager: Device '{0}' does not exist in manager. Cannot remove", newDev.Key); + } + + public static IEnumerable GetDeviceKeys() + { + //return _Devices.Select(d => d.Key).ToList(); + return Devices.Keys; + } + + public static IEnumerable 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]; + + return null; + } + + /// + /// Console handler that simulates com port data receive + /// + /// + public static void SimulateComReceiveOnDevice(string s) + { + // devcomsim:1 xyzabc + var match = Regex.Match(s, @"(\S*)\s*(.*)"); + if (match.Groups.Count < 3) + { + CrestronConsole.ConsoleCommandResponse(" Format: devsimreceive:P "); + return; + } + //Debug.Console(2, "**** {0} - {1} ****", match.Groups[1].Value, match.Groups[2].Value); + + ComPortController 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; + } + com.SimulateReceive(match.Groups[2].Value); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DisplayUiConstants.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DisplayUiConstants.cs new file mode 100644 index 00000000..a42239fc --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DisplayUiConstants.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Integers that represent the "source type number" for given sources. + /// Primarily used by the UI to calculate subpage join offsets + /// Note, for UI, only values 1-49 are valid. + /// + public class DisplayUiConstants + { + public const uint TypeRadio = 1; + public const uint TypeDirecTv = 9; + public const uint TypeBluray = 13; + public const uint TypeChromeTv = 15; + public const uint TypeFireTv = 16; + public const uint TypeAppleTv = 17; + public const uint TypeRoku = 18; + public const uint TypeLaptop = 31; + public const uint TypePc = 32; + + public const uint TypeNoControls = 49; + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/FIND HOMES Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/FIND HOMES Interfaces.cs new file mode 100644 index 00000000..318f5179 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/FIND HOMES Interfaces.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + public interface IOnline + { + BoolFeedback IsOnline { get; } + } + + ///// + ///// ** WANT THIS AND ALL ITS FRIENDS TO GO AWAY ** + ///// Defines a class that has a list of CueAction objects, typically + ///// for linking functions to user interfaces or API calls + ///// + //public interface IHasCueActionList + //{ + // List CueActionList { get; } + //} + + + //public interface IHasComPortsHardware + //{ + // IComPorts ComPortsDevice { get; } + //} + + /// + /// Describes a device that can have a video sync providing device attached to it + /// + public interface IAttachVideoStatus : IKeyed + { + // Extension methods will depend on this + } + + /// + /// For display classes that can provide usage data + /// + public interface IDisplayUsage + { + IntFeedback LampHours { get; } + } + + public interface IMakeModel : IKeyed + { + string DeviceMake { get; } + string DeviceModel { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/GenericMonitoredTcpDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/GenericMonitoredTcpDevice.cs new file mode 100644 index 00000000..e7267782 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/GenericMonitoredTcpDevice.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core.Devices +{ + public class GenericCommunicationMonitoredDevice : Device, ICommunicationMonitor + { + IBasicCommunication Client; + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + public GenericCommunicationMonitoredDevice(string key, string name, IBasicCommunication comm, string pollString, + long pollTime, long warningTime, long errorTime) + : base(key, name) + { + Client = comm; + CommunicationMonitor = new GenericCommunicationMonitor(this, Client, pollTime, warningTime, errorTime, pollString); + + // ------------------------------------------------------DELETE THIS + CommunicationMonitor.StatusChange += (o, a) => + { + Debug.Console(2, this, "Communication monitor status change: {0}", a.Status); + }; + + } + + public GenericCommunicationMonitoredDevice(string key, string name, IBasicCommunication comm, string pollString) + : this(key, name, comm, pollString, 30000, 120000, 300000) + { + } + + + /// + /// Generic monitor for TCP reachability. Default with 30s poll, 120s warning and 300s error times + /// + [Obsolete] + public GenericCommunicationMonitoredDevice(string key, string name, string hostname, int port, string pollString) + : this(key, name, hostname, port, pollString, 30000, 120000, 300000) + { + } + + /// + /// Monitor for TCP reachability + /// + [Obsolete] + public GenericCommunicationMonitoredDevice(string key, string name, string hostname, int port, string pollString, + long pollTime, long warningTime, long errorTime) + : base(key, name) + { + Client = new GenericTcpIpClient(key + "-tcp", hostname, port, 512); + CommunicationMonitor = new GenericCommunicationMonitor(this, Client, pollTime, warningTime, errorTime, pollString); + + // ------------------------------------------------------DELETE THIS + CommunicationMonitor.StatusChange += (o, a) => + { + Debug.Console(2, this, "Communication monitor status change: {0}", a.Status); + }; + } + + + public override bool CustomActivate() + { + CommunicationMonitor.Start(); + return true; + } + + public override bool Deactivate() + { + CommunicationMonitor.Stop(); + return true; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IAttachVideoStatusExtensions.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IAttachVideoStatusExtensions.cs new file mode 100644 index 00000000..e5218ee0 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IAttachVideoStatusExtensions.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + public static class IAttachVideoStatusExtensions + { + /// + /// Gets the VideoStatusOutputs for the device + /// + /// + /// Attached VideoStatusOutputs or the default if none attached + public static VideoStatusOutputs GetVideoStatuses(this IAttachVideoStatus attachedDev) + { + // See if this device is connected to a status-providing port + var tl = TieLineCollection.Default.FirstOrDefault(t => + t.SourcePort.ParentDevice == attachedDev + && t.DestinationPort is RoutingInputPortWithVideoStatuses); + if (tl != null) + { + // if so, and it's got status, return it -- or null + var port = tl.DestinationPort as RoutingInputPortWithVideoStatuses; + if (port != null) + return port.VideoStatus; + } + return VideoStatusOutputs.NoStatus; + } + + public static bool HasVideoStatuses(this IAttachVideoStatus attachedDev) + { + return TieLineCollection.Default.FirstOrDefault(t => + t.SourcePort.ParentDevice == attachedDev + && t.DestinationPort is RoutingInputPortWithVideoStatuses) != null; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks.cs new file mode 100644 index 00000000..a21d2a3a --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharp.Reflection; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public interface IHasFeedback : IKeyed + { + /// + /// This method shall return a list of all Output objects on a device, + /// including all "aggregate" devices. + /// + FeedbackCollection Feedbacks { get; } + + } + + + public static class IHasFeedbackExtensions + { + public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates) + { + 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)); + + + + var feedbacks = source.Feedbacks.OrderBy(x => x.Type); + if (feedbacks != null) + { + Debug.Console(0, source, "\n\nAvailable feedbacks:"); + foreach (var f in feedbacks) + { + string val = ""; + string type = ""; + if (getCurrentStates) + { + if (f is BoolFeedback) + { + val = f.BoolValue.ToString(); + type = "boolean"; + } + else if (f is IntFeedback) + { + val = f.IntValue.ToString(); + type = "integer"; + } + else if (f is StringFeedback) + { + val = f.StringValue; + type = "string"; + } + } + Debug.Console(0, "{0,-12} {1, -25} {2}", type, + (string.IsNullOrEmpty(f.Key) ? "-no key-" : f.Key), val); + } + } + else + Debug.Console(0, source, "No available outputs:"); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_BACKUP_3692.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_BACKUP_3692.cs new file mode 100644 index 00000000..8af64e92 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_BACKUP_3692.cs @@ -0,0 +1,135 @@ +<<<<<<< HEAD +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharp.Reflection; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public interface IHasFeedback : IKeyed + { + /// + /// This method shall return a list of all Output objects on a device, + /// including all "aggregate" devices. + /// + List Feedbacks { get; } + + } + + + public static class IHasFeedbackExtensions + { + public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates) + { + 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)); + + + + var feedbacks = source.Feedbacks.OrderBy(x => x.Type); + if (feedbacks != null) + { + Debug.Console(0, source, "\n\nAvailable feedbacks:"); + foreach (var f in feedbacks) + { + string val = ""; + string type = ""; + if (getCurrentStates) + { + if (f is BoolFeedback) + { + val = f.BoolValue.ToString(); + type = "boolean"; + } + else if (f is IntFeedback) + { + val = f.IntValue.ToString(); + type = "integer"; + } + else if (f is StringFeedback) + { + val = f.StringValue; + type = "string"; + } + } + Debug.Console(0, "{0,-12} {1, -25} {2}", type, + (string.IsNullOrEmpty(f.Cue.Name) ? "-no name-" : f.Cue.Name), val); + } + } + else + Debug.Console(0, source, "No available outputs:"); + } + } +======= +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public interface IHasFeedback : IKeyed + { + /// + /// This method shall return a list of all Output objects on a device, + /// including all "aggregate" devices. + /// + List Feedbacks { get; } + + } + + + public static class IHasFeedbackExtensions + { + public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates) + { + var feedbacks = source.Feedbacks.OrderBy(x => x.Type); + if (feedbacks != null) + { + Debug.Console(0, source, "\n\nAvailable feedbacks:"); + foreach (var f in feedbacks) + { + string val = ""; + if (getCurrentStates) + { + if (f is BoolFeedback) + val = " = " + f.BoolValue; + else if(f is IntFeedback) + val = " = " + f.IntValue; + else if(f is StringFeedback) + val = " = " + f.StringValue; + + //switch (f.Type) + //{ + // case eCueType.Bool: + // val = " = " + f.BoolValue; + // break; + // case eCueType.Int: + // val = " = " + f.IntValue; + // break; + // case eCueType.String: + // val = " = " + f.StringValue; + // break; + // //case eOutputType.Other: + // // break; + //} + } + Debug.Console(0, "{0,-8} {1}{2}", f.GetType(), + (string.IsNullOrEmpty(f.Cue.Name) ? "-none-" : f.Cue.Name), val); + } + } + else + Debug.Console(0, source, "No available outputs:"); + } + } + + public static class IHasFeedbackFusionExtensions + { + + } +>>>>>>> origin/feature/ecs-307 +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_BASE_3692.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_BASE_3692.cs new file mode 100644 index 00000000..446577f6 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_BASE_3692.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public interface IHasFeedback : IKeyed + { + /// + /// This method shall return a list of all Output objects on a device, + /// including all "aggregate" devices. + /// + List Feedbacks { get; } + + } + + + public static class IHasFeedbackExtensions + { + public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates) + { + var feedbacks = source.Feedbacks.OrderBy(x => x.Type); + if (feedbacks != null) + { + Debug.Console(0, source, "\n\nAvailable feedbacks:"); + foreach (var f in feedbacks) + { + string val = ""; + if (getCurrentStates) + { + if (f is BoolFeedback) + val = " = " + f.BoolValue; + else if(f is IntFeedback) + val = " = " + f.IntValue; + else if(f is StringFeedback) + val = " = " + f.StringValue; + + //switch (f.Type) + //{ + // case eCueType.Bool: + // val = " = " + f.BoolValue; + // break; + // case eCueType.Int: + // val = " = " + f.IntValue; + // break; + // case eCueType.String: + // val = " = " + f.StringValue; + // break; + // //case eOutputType.Other: + // // break; + //} + } + Debug.Console(0, "{0,-8} {1}{2}", f.GetType(), + (string.IsNullOrEmpty(f.Cue.Name) ? "-none-" : f.Cue.Name), val); + } + } + else + Debug.Console(0, source, "No available outputs:"); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_LOCAL_3692.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_LOCAL_3692.cs new file mode 100644 index 00000000..5d33afa9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_LOCAL_3692.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharp.Reflection; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public interface IHasFeedback : IKeyed + { + /// + /// This method shall return a list of all Output objects on a device, + /// including all "aggregate" devices. + /// + List Feedbacks { get; } + + } + + + public static class IHasFeedbackExtensions + { + public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates) + { + 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)); + + + + var feedbacks = source.Feedbacks.OrderBy(x => x.Type); + if (feedbacks != null) + { + Debug.Console(0, source, "\n\nAvailable feedbacks:"); + foreach (var f in feedbacks) + { + string val = ""; + string type = ""; + if (getCurrentStates) + { + if (f is BoolFeedback) + { + val = f.BoolValue.ToString(); + type = "boolean"; + } + else if (f is IntFeedback) + { + val = f.IntValue.ToString(); + type = "integer"; + } + else if (f is StringFeedback) + { + val = f.StringValue; + type = "string"; + } + } + Debug.Console(0, "{0,-12} {1, -25} {2}", type, + (string.IsNullOrEmpty(f.Cue.Name) ? "-no name-" : f.Cue.Name), val); + } + } + else + Debug.Console(0, source, "No available outputs:"); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_REMOTE_3692.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_REMOTE_3692.cs new file mode 100644 index 00000000..1ac02c71 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IHasFeedbacks_REMOTE_3692.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public interface IHasFeedback : IKeyed + { + /// + /// This method shall return a list of all Output objects on a device, + /// including all "aggregate" devices. + /// + List Feedbacks { get; } + + } + + + public static class IHasFeedbackExtensions + { + public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates) + { + var feedbacks = source.Feedbacks.OrderBy(x => x.Type); + if (feedbacks != null) + { + Debug.Console(0, source, "\n\nAvailable feedbacks:"); + foreach (var f in feedbacks) + { + string val = ""; + if (getCurrentStates) + { + if (f is BoolFeedback) + val = " = " + f.BoolValue; + else if(f is IntFeedback) + val = " = " + f.IntValue; + else if(f is StringFeedback) + val = " = " + f.StringValue; + + //switch (f.Type) + //{ + // case eCueType.Bool: + // val = " = " + f.BoolValue; + // break; + // case eCueType.Int: + // val = " = " + f.IntValue; + // break; + // case eCueType.String: + // val = " = " + f.StringValue; + // break; + // //case eOutputType.Other: + // // break; + //} + } + Debug.Console(0, "{0,-8} {1}{2}", f.GetType(), + (string.IsNullOrEmpty(f.Cue.Name) ? "-none-" : f.Cue.Name), val); + } + } + else + Debug.Console(0, source, "No available outputs:"); + } + } + + public static class IHasFeedbackFusionExtensions + { + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IUsageTracking.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IUsageTracking.cs new file mode 100644 index 00000000..05b59366 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IUsageTracking.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public interface IUsageTracking + { + UsageTracking UsageTracker { get; set; } + } + + //public static class IUsageTrackingExtensions + //{ + // public static void EnableUsageTracker(this IUsageTracking device) + // { + // device.UsageTracker = new UsageTracking(); + // } + //} + + public class UsageTracking + { + public event EventHandler DeviceUsageEnded; + + public InUseTracking InUseTracker { get; protected set; } + + public bool UsageIsTracked { get; set; } + + public bool UsageTrackingStarted { get; protected set; } + public DateTime UsageStartTime { get; protected set; } + public DateTime UsageEndTime { get; protected set; } + + public Device Parent { get; private set; } + + public UsageTracking(Device parent) + { + Parent = parent; + + InUseTracker = new InUseTracking(); + + InUseTracker.InUseFeedback.OutputChange += InUseFeedback_OutputChange; //new EventHandler(); + } + + void InUseFeedback_OutputChange(object sender, EventArgs e) + { + if(InUseTracker.InUseFeedback.BoolValue) + { + StartDeviceUsage(); + } + else + { + EndDeviceUsage(); + } + } + + + /// + /// Stores the usage start time + /// + public void StartDeviceUsage() + { + UsageTrackingStarted = true; + UsageStartTime = DateTime.Now; + } + + /// + /// Calculates the difference between the usage start and end times, gets the total minutes used and fires an event to pass that info to a consumer + /// + public void EndDeviceUsage() + { + try + { + UsageTrackingStarted = false; + + UsageEndTime = DateTime.Now; + + if (UsageStartTime != null) + { + var timeUsed = UsageEndTime - UsageStartTime; + + var handler = DeviceUsageEnded; + + if (handler != null) + { + Debug.Console(1, "Device Usage Ended for: {0} at {1}. In use for {2} minutes.", Parent.Name, UsageEndTime, timeUsed.Minutes); + handler(this, new DeviceUsageEventArgs() { UsageEndTime = UsageEndTime, MinutesUsed = timeUsed.Minutes }); + } + } + } + catch (Exception e) + { + Debug.Console(1, "Error ending device usage: {0}", e); + } + } + } + + public class DeviceUsageEventArgs : EventArgs + { + public DateTime UsageEndTime { get; set; } + public int MinutesUsed { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IUsageTracking.cs.orig b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IUsageTracking.cs.orig new file mode 100644 index 00000000..59019df5 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IUsageTracking.cs.orig @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public interface IUsageTracking + { + UsageTracking UsageTracker { get; set; } + } + + //public static class IUsageTrackingExtensions + //{ + // public static void EnableUsageTracker(this IUsageTracking device) + // { + // device.UsageTracker = new UsageTracking(); + // } + //} + + public class UsageTracking + { + public event EventHandler DeviceUsageEnded; + + public InUseTracking InUseTracker { get; protected set; } + + public bool UsageIsTracked { get; set; } + public DateTime UsageStartTime { get; protected set; } + public DateTime UsageEndTime { get; protected set; } + + public Device Parent { get; private set; } + + public UsageTracking(Device parent) + { + Parent = parent; + + InUseTracker = new InUseTracking(); + + InUseTracker.InUseFeedback.OutputChange +=new EventHandler(InUseFeedback_OutputChange); + } + + void InUseFeedback_OutputChange(object sender, EventArgs e) + { + if(InUseTracker.InUseFeedback.BoolValue) + { + StartDeviceUsage(); + } + else + { + EndDeviceUsage(); + } + } + + + /// + /// Stores the usage start time + /// + public void StartDeviceUsage() + { + UsageStartTime = DateTime.Now; + } + + /// + /// Calculates the difference between the usage start and end times, gets the total minutes used and fires an event to pass that info to a consumer + /// + public void EndDeviceUsage() + { + try + { + UsageEndTime = DateTime.Now; + + if (UsageStartTime != null) + { + var timeUsed = UsageEndTime - UsageStartTime; + + var handler = DeviceUsageEnded; + + if (handler != null) + { + Debug.Console(1, "Device Usage Ended for: {0} at {1}. In use for {2} minutes.", Parent.Name, UsageEndTime, timeUsed.Minutes); + handler(this, new DeviceUsageEventArgs() { UsageEndTime = UsageEndTime, MinutesUsed = timeUsed.Minutes }); + } + } + } + catch (Exception e) + { +<<<<<<< HEAD + Debug.Console(1, "Device Usage Ended at {0}. In use for {1} minutes.", UsageEndTime, timeUsed.Minutes); + handler(this, new DeviceUsageEventArgs() { UsageEndTime = UsageEndTime, MinutesUsed = timeUsed.Minutes }); +======= + Debug.Console(1, "Error ending device usage: {0}", e); +>>>>>>> origin/feature/fusion-nyu + } + } + } + + public class DeviceUsageEventArgs : EventArgs + { + public DateTime UsageEndTime { get; set; } + public int MinutesUsed { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IVolumeAndAudioInterfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IVolumeAndAudioInterfaces.cs new file mode 100644 index 00000000..1cff62fc --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IVolumeAndAudioInterfaces.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Defines minimal volume control methods + /// + public interface IBasicVolumeControls + { + void VolumeUp(bool pressRelease); + void VolumeDown(bool pressRelease); + void MuteToggle(); + } + + /// + /// Adds feedback and direct volume level set to IBasicVolumeControls + /// + public interface IBasicVolumeWithFeedback : IBasicVolumeControls + { + void SetVolume(ushort level); + void MuteOn(); + void MuteOff(); + IntFeedback VolumeLevelFeedback { get; } + BoolFeedback MuteFeedback { get; } + } + + /// + /// A class that implements this contains a reference to a current IBasicVolumeControls device. + /// The class may have multiple IBasicVolumeControls. + /// + public interface IHasCurrentVolumeControls + { + IBasicVolumeControls CurrentVolumeControls { get; } + event EventHandler CurrentVolumeDeviceChange; + } + + + /// + /// + /// + public interface IFullAudioSettings : IBasicVolumeWithFeedback + { + void SetBalance(ushort level); + void BalanceLeft(bool pressRelease); + void BalanceRight(bool pressRelease); + + void SetBass(ushort level); + void BassUp(bool pressRelease); + void BassDown(bool pressRelease); + + void SetTreble(ushort level); + void TrebleUp(bool pressRelease); + void TrebleDown(bool pressRelease); + + bool hasMaxVolume { get; } + void SetMaxVolume(ushort level); + void MaxVolumeUp(bool pressRelease); + void MaxVolumeDown(bool pressRelease); + + bool hasDefaultVolume { get; } + void SetDefaultVolume(ushort level); + void DefaultVolumeUp(bool pressRelease); + void DefaultVolumeDown(bool pressRelease); + + void LoudnessToggle(); + void MonoToggle(); + + BoolFeedback LoudnessFeedback { get; } + BoolFeedback MonoFeedback { get; } + IntFeedback BalanceFeedback { get; } + IntFeedback BassFeedback { get; } + IntFeedback TrebleFeedback { get; } + IntFeedback MaxVolumeFeedback { get; } + IntFeedback DefaultVolumeFeedback { get; } + } + + /// + /// A class that implements this, contains a reference to an IBasicVolumeControls device. + /// For example, speakers attached to an audio zone. The speakers can provide reference + /// to their linked volume control. + /// + public interface IHasVolumeDevice + { + IBasicVolumeControls VolumeDevice { get; } + } + + /// + /// Identifies a device that contains audio zones + /// + public interface IAudioZones : IRouting + { + Dictionary Zone { get; } + } + + /// + /// Defines minimum functionality for an audio zone + /// + public interface IAudioZone : IBasicVolumeWithFeedback + { + void SelectInput(ushort input); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs new file mode 100644 index 00000000..c67afe1c --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IrOutputPortController.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + + /// + /// IR port wrapper. May act standalone + /// + public class IrOutputPortController : Device + { + uint IrPortUid; + IROutputPort IrPort; + + public ushort StandardIrPulseTime { get; set; } + public string DriverFilepath { get; private set; } + public bool DriverIsLoaded { get; private set; } + + /// + /// Constructor for IrDevice base class. If a null port is provided, this class will + /// still function without trying to talk to a port. + /// + public IrOutputPortController(string key, IROutputPort port, string irDriverFilepath) + : base(key) + { + //if (port == null) throw new ArgumentNullException("port"); + IrPort = port; + if (port == null) + { + Debug.Console(0, this, "WARNING No valid IR Port assigned to controller. IR will not function"); + return; + } + LoadDriver(irDriverFilepath); + } + + /// + /// Loads the IR driver at path + /// + /// + public void LoadDriver(string path) + { + if (string.IsNullOrEmpty(path)) path = DriverFilepath; + try + { + IrPortUid = IrPort.LoadIRDriver(path); + DriverFilepath = path; + StandardIrPulseTime = 200; + DriverIsLoaded = true; + } + catch + { + DriverIsLoaded = false; + var message = string.Format("WARNING IR Driver '{0}' failed to load", path); + Debug.Console(0, this, message); + ErrorLog.Error(message); + } + } + + + /// + /// Starts and stops IR command on driver. Safe for missing commands + /// + public virtual void PressRelease(string command, bool state) + { + Debug.Console(2, this, "IR:'{0}'={1}", command, state); + if (IrPort == null) + { + Debug.Console(2, this, "WARNING No IR Port assigned to controller"); + return; + } + if (!DriverIsLoaded) + { + Debug.Console(2, this, "WARNING IR driver is not loaded"); + return; + } + if (state) + { + if (IrPort.IsIRCommandAvailable(IrPortUid, command)) + IrPort.Press(IrPortUid, command); + else + NoIrCommandError(command); + } + else + IrPort.Release(); + } + + /// + /// Pulses a command on driver. Safe for missing commands + /// + public virtual void Pulse(string command, ushort time) + { + if (IrPort == null) + { + Debug.Console(2, this, "WARNING No IR Port assigned to controller"); + return; + } + if (!DriverIsLoaded) + { + Debug.Console(2, this, "WARNING IR driver is not loaded"); + return; + } + if (IrPort.IsIRCommandAvailable(IrPortUid, command)) + IrPort.PressAndRelease(IrPortUid, command, time); + else + NoIrCommandError(command); + } + + /// + /// Notifies the console when a bad command is used. + /// + protected void NoIrCommandError(string command) + { + Debug.Console(2, this, "Device {0}: IR Driver {1} does not contain command {2}", + Key, IrPort.IRDriverFileNameByIRDriverId(IrPortUid), command); + } + + + ///// + ///// When fed a dictionary of uint, string, will return UOs for each item, + ///// attached to this IrOutputPort + ///// + ///// + ///// + //public List GetUOsForIrCommands(Dictionary numStringDict) + //{ + // var funcs = new List(numStringDict.Count); + // foreach (var kvp in numStringDict) + // { + // // Have to assign these locally because of kvp's scope + // var cue = kvp.Key; + // var command = kvp.Value; + // funcs.Add(new BoolCueActionPair(cue, b => this.PressRelease(command, b))); + // } + // return funcs; + //} + + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/NewInterfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/NewInterfaces.cs new file mode 100644 index 00000000..ca6524f4 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/NewInterfaces.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +namespace PepperDash.Essentials.Core +{ + //public interface IVolumeFunctions + //{ + // BoolCueActionPair VolumeUpCueActionPair { get; } + // BoolCueActionPair VolumeDownCueActionPair { get; } + // BoolCueActionPair MuteToggleCueActionPair { get; } + //} + + //public interface IVolumeTwoWay : IVolumeFunctions + //{ + // IntFeedback VolumeLevelFeedback { get; } + // UShortCueActionPair VolumeLevelCueActionPair { get; } + // BoolFeedback IsMutedFeedback { get; } + //} + + ///// + ///// + ///// + //public static class IFunctionListExtensions + //{ + // public static string GetFunctionsConsoleList(this IHasCueActionList device) + // { + // var sb = new StringBuilder(); + // var list = device.CueActionList; + // foreach (var cap in list) + // sb.AppendFormat("{0,-15} {1,4} {2}\r", cap.Cue.Name, cap.Cue.Number, cap.GetType().Name); + // return sb.ToString(); + // } + //} + + public enum AudioChangeType + { + Mute, Volume + } + + public class AudioChangeEventArgs + { + public AudioChangeType ChangeType { get; private set; } + public IBasicVolumeControls AudioDevice { get; private set; } + + public AudioChangeEventArgs(IBasicVolumeControls device, AudioChangeType changeType) + { + ChangeType = changeType; + AudioDevice = device; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/PresentationDeviceType.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/PresentationDeviceType.cs new file mode 100644 index 00000000..adb427fb --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/PresentationDeviceType.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.EthernetCommunication; +using Crestron.SimplSharpPro.UI; + +namespace PepperDash.Essentials.Core +{ + //[Obsolete] + //public class PresentationDeviceType + //{ + // public const ushort Default = 1; + // public const ushort CableSetTopBox = 2; + // public const ushort SatelliteSetTopBox = 3; + // public const ushort Dvd = 4; + // public const ushort Bluray = 5; + // public const ushort PC = 9; + // public const ushort Laptop = 10; + //} + + public enum PresentationSourceType + { + None, Dvd, Laptop, PC, SetTopBox, VCR + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/REMOVE IHasFeedbacks.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/REMOVE IHasFeedbacks.cs new file mode 100644 index 00000000..511a952b --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/REMOVE IHasFeedbacks.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + //public interface IHasFeedback : IKeyed + //{ + // /// + // /// This method shall return a list of all Output objects on a device, + // /// including all "aggregate" devices. + // /// + // List Feedbacks { get; } + + //} + + + //public static class IHasFeedbackExtensions + //{ + // public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates) + // { + // var outputs = source.Feedbacks.OrderBy(x => x.Type); + // if (outputs != null) + // { + // Debug.Console(0, source, "\n\nAvailable outputs:"); + // foreach (var o in outputs) + // { + // string val = ""; + // if (getCurrentStates) + // { + // switch (o.Type) + // { + // case eCueType.Bool: + // val = " = " + o.BoolValue; + // break; + // case eCueType.Int: + // val = " = " + o.IntValue; + // break; + // case eCueType.String: + // val = " = " + o.StringValue; + // break; + // //case eOutputType.Other: + // // break; + // } + // } + // Debug.Console(0, "{0,-8} {1,5} {2}{3}", o.Type, o.Cue.Number, + // (string.IsNullOrEmpty(o.Cue.Name) ? "-none-" : o.Cue.Name), val); + // } + // } + // else + // Debug.Console(0, source, "No available outputs:"); + // } + //} +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/ReconfigurableDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/ReconfigurableDevice.cs new file mode 100644 index 00000000..3acfcac0 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/ReconfigurableDevice.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.Core.Devices +{ + /// + /// + /// + public abstract class ReconfigurableDevice : Device + { + public event EventHandler ConfigChanged; + + public DeviceConfig Config { get; private set; } + + public ReconfigurableDevice(DeviceConfig config) + : base(config.Key) + { + SetNameHelper(config); + + Config = config; + } + + /// + /// Sets the Config, calls CustomSetConfig and fires the ConfigChanged event + /// + /// + public void SetConfig(DeviceConfig config) + { + Config = config; + + SetNameHelper(config); + + CustomSetConfig(config); + + var handler = ConfigChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + + void SetNameHelper(DeviceConfig config) + { + if (!string.IsNullOrEmpty(config.Name)) + Name = config.Name; + } + + /// + /// Used by the extending class to allow for any custom actions to be taken (tell the ConfigWriter to write config, etc) + /// + /// + protected virtual void CustomSetConfig(DeviceConfig config) + { + ConfigWriter.UpdateDeviceConfig(config); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/SmartObjectBaseTypes.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/SmartObjectBaseTypes.cs new file mode 100644 index 00000000..37e62036 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/SmartObjectBaseTypes.cs @@ -0,0 +1,11 @@ + +namespace PepperDash.Essentials.Core +{ + public class SmartObjectJoinOffsets + { + public const ushort Dpad = 1; + public const ushort Numpad = 2; + + public const ushort PresetList = 6; + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/SourceListItem.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/SourceListItem.cs new file mode 100644 index 00000000..86c940d3 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/SourceListItem.cs @@ -0,0 +1,115 @@ +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 +{ + /// + /// + /// + public enum eSourceListItemType + { + Route, Off, Other, SomethingAwesomerThanThese + } + + /// + /// Represents an item in a source list - can be deserialized into. + /// + public class SourceListItem + { + [JsonProperty("sourceKey")] + public string SourceKey { get; set; } + + /// + /// Returns the source Device for this, if it exists in DeviceManager + /// + [JsonIgnore] + public Device SourceDevice + { + get + { + if (_SourceDevice == null) + _SourceDevice = DeviceManager.GetDeviceForKey(SourceKey) as Device; + return _SourceDevice; + } + } + Device _SourceDevice; + + /// + /// Gets either the source's Name or this AlternateName property, if + /// defined. If source doesn't exist, returns "Missing source" + /// + [JsonProperty("preferredName")] + public string PreferredName + { + get + { + if (string.IsNullOrEmpty(Name)) + { + if (SourceDevice == null) + return "---"; + return SourceDevice.Name; + } + return Name; + } + } + + /// + /// A name that will override the source's name on the UI + /// + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("icon")] + public string Icon { get; set; } + + [JsonProperty("altIcon")] + public string AltIcon { get; set; } + + [JsonProperty("includeInSourceList")] + public bool IncludeInSourceList { get; set; } + + [JsonProperty("order")] + public int Order { get; set; } + + [JsonProperty("volumeControlKey")] + public string VolumeControlKey { get; set; } + + [JsonProperty("type")] + [JsonConverter(typeof(StringEnumConverter))] + public eSourceListItemType Type { get; set; } + + [JsonProperty("routeList")] + public List RouteList { get; set; } + + [JsonProperty("disableCodecSharing")] + public bool DisableCodecSharing { get; set; } + + [JsonProperty("disableRoutedSharing")] + public bool DisableRoutedSharing { get; set; } + + public SourceListItem() + { + Icon = "Blank"; + } + } + + public class SourceRouteListItem + { + [JsonProperty("sourceKey")] + public string SourceKey { get; set; } + + [JsonProperty("destinationKey")] + public string DestinationKey { get; set; } + + [JsonProperty("type")] + public eRoutingSignalType Type { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/VolumeDeviceChangeEventArgs.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/VolumeDeviceChangeEventArgs.cs new file mode 100644 index 00000000..2171cb73 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/VolumeDeviceChangeEventArgs.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public class VolumeDeviceChangeEventArgs : EventArgs + { + public IBasicVolumeControls OldDev { get; private set; } + public IBasicVolumeControls NewDev { get; private set; } + public ChangeType Type { get; private set; } + + public VolumeDeviceChangeEventArgs(IBasicVolumeControls oldDev, IBasicVolumeControls newDev, ChangeType type) + { + OldDev = oldDev; + NewDev = newDev; + Type = type; + } + } + + /// + /// + /// + public enum ChangeType + { + WillChange, DidChange + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs new file mode 100644 index 00000000..b22f04d0 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Core +{ + public class BasicIrDisplay : DisplayBase, IBasicVolumeControls, IPower, IWarmingCooling, IRoutingSinkWithSwitching + { + public IrOutputPortController IrPort { get; private set; } + public ushort IrPulseTime { get; set; } + + protected override Func PowerIsOnFeedbackFunc + { + get { return () => _PowerIsOn; } + } + protected override Func IsCoolingDownFeedbackFunc + { + get { return () => _IsCoolingDown; } + } + protected override Func IsWarmingUpFeedbackFunc + { + get { return () => _IsWarmingUp; } + } + + bool _PowerIsOn; + bool _IsWarmingUp; + bool _IsCoolingDown; + + public BasicIrDisplay(string key, string name, IROutputPort port, string irDriverFilepath) + : base(key, name) + { + IrPort = new IrOutputPortController(key + "-ir", port, irDriverFilepath); + DeviceManager.AddDevice(IrPort); + + PowerIsOnFeedback.OutputChange += (o, a) => { + Debug.Console(2, this, "Power on={0}", _PowerIsOn); + if (_PowerIsOn) StartWarmingTimer(); + else StartCoolingTimer(); + }; + IsWarmingUpFeedback.OutputChange += (o, a) => Debug.Console(2, this, "Warming up={0}", _IsWarmingUp); + IsCoolingDownFeedback.OutputChange += (o, a) => Debug.Console(2, this, "Cooling down={0}", _IsCoolingDown); + + InputPorts.AddRange(new RoutingPortCollection + { + new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(Hdmi1), this, false), + new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(Hdmi2), this, false), + new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(Hdmi3), this, false), + new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(Hdmi4), this, false), + new RoutingInputPort(RoutingPortNames.ComponentIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(Component1), this, false), + new RoutingInputPort(RoutingPortNames.CompositeIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(Video1), this, false), + new RoutingInputPort(RoutingPortNames.AntennaIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(Antenna), this, false), + }); + } + + public void Hdmi1() + { + IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_1, IrPulseTime); + } + + public void Hdmi2() + { + IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_2, IrPulseTime); + } + + public void Hdmi3() + { + IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_3, IrPulseTime); + } + + public void Hdmi4() + { + IrPort.Pulse(IROutputStandardCommands.IROut_HDMI_4, IrPulseTime); + } + + public void Component1() + { + IrPort.Pulse(IROutputStandardCommands.IROut_COMPONENT_1, IrPulseTime); + } + + public void Video1() + { + IrPort.Pulse(IROutputStandardCommands.IROut_VIDEO_1, IrPulseTime); + } + + public void Antenna() + { + IrPort.Pulse(IROutputStandardCommands.IROut_ANTENNA, IrPulseTime); + } + + #region IPower Members + + public override void PowerOn() + { + IrPort.Pulse(IROutputStandardCommands.IROut_POWER_ON, IrPulseTime); + _PowerIsOn = true; + PowerIsOnFeedback.FireUpdate(); + } + + public override void PowerOff() + { + _PowerIsOn = false; + PowerIsOnFeedback.FireUpdate(); + IrPort.Pulse(IROutputStandardCommands.IROut_POWER_OFF, IrPulseTime); + } + + public override void PowerToggle() + { + _PowerIsOn = false; + PowerIsOnFeedback.FireUpdate(); + IrPort.Pulse(IROutputStandardCommands.IROut_POWER, IrPulseTime); + } + + #endregion + + #region IBasicVolumeControls Members + + public void VolumeUp(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_VOL_PLUS, pressRelease); + } + + public void VolumeDown(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_VOL_MINUS, pressRelease); + } + + public void MuteToggle() + { + IrPort.Pulse(IROutputStandardCommands.IROut_MUTE, 200); + } + + #endregion + + void StartWarmingTimer() + { + _IsWarmingUp = true; + IsWarmingUpFeedback.FireUpdate(); + new CTimer(o => { + _IsWarmingUp = false; + IsWarmingUpFeedback.FireUpdate(); + }, 10000); + } + + void StartCoolingTimer() + { + _IsCoolingDown = true; + IsCoolingDownFeedback.FireUpdate(); + new CTimer(o => + { + _IsCoolingDown = false; + IsCoolingDownFeedback.FireUpdate(); + }, 7000); + } + + #region IRoutingSink Members + + /// + /// Typically called by the discovery routing algorithm. + /// + /// A delegate containing the input selector method to call + public override void ExecuteSwitch(object inputSelector) + { + Debug.Console(2, this, "Switching to input '{0}'", (inputSelector as Action).ToString()); + + Action finishSwitch = () => + { + var action = inputSelector as Action; + if (action != null) + action(); + }; + + if (!PowerIsOnFeedback.BoolValue) + { + PowerOn(); + EventHandler oneTimer = null; + oneTimer = (o, a) => + { + if (IsWarmingUpFeedback.BoolValue) return; // Only catch done warming + IsWarmingUpFeedback.OutputChange -= oneTimer; + finishSwitch(); + }; + IsWarmingUpFeedback.OutputChange += oneTimer; + } + else // Do it! + finishSwitch(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DELETE IRDisplayBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DELETE IRDisplayBase.cs new file mode 100644 index 00000000..06b4da07 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DELETE IRDisplayBase.cs @@ -0,0 +1,105 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; +//using Crestron.SimplSharpPro; + +//namespace PepperDash.Essentials.Core +//{ +// public abstract class IRDisplayBase : DisplayBase, IHasCueActionList +// { +// public IrOutputPortController IrPort { get; private set; } +// /// +// /// Default to 200ms +// /// +// public ushort IrPulseTime { get; set; } +// bool _PowerIsOn; +// bool _IsWarmingUp; +// bool _IsCoolingDown; + +// /// +// /// FunctionList is pre-defined to have power commands. +// /// +// public IRDisplayBase(string key, string name, IROutputPort port, string irDriverFilepath) +// : base(key, name) +// { +// IrPort = new IrOutputPortController("ir-" + key, port, irDriverFilepath); +// IrPulseTime = 200; +// WarmupTime = 7000; +// CooldownTime = 10000; + +// CueActionList = new List +// { +// new BoolCueActionPair(CommonBoolCue.Power, b=> PowerToggle()), +// new BoolCueActionPair(CommonBoolCue.PowerOn, b=> PowerOn()), +// new BoolCueActionPair(CommonBoolCue.PowerOff, b=> PowerOff()), +// }; +// } + +// public override void PowerOn() +// { +// if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown) +// { +// _IsWarmingUp = true; +// IsWarmingUpFeedback.FireUpdate(); +// // Fake power-up cycle +// WarmupTimer = new CTimer(o => +// { +// _IsWarmingUp = false; +// _PowerIsOn = true; +// IsWarmingUpFeedback.FireUpdate(); +// PowerIsOnFeedback.FireUpdate(); +// }, WarmupTime); +// } +// IrPort.Pulse(IROutputStandardCommands.IROut_POWER_ON, IrPulseTime); +// } + +// 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; +// _PowerIsOn = false; +// PowerIsOnFeedback.FireUpdate(); +// IsCoolingDownFeedback.FireUpdate(); +// // Fake cool-down cycle +// CooldownTimer = new CTimer(o => +// { +// _IsCoolingDown = false; +// IsCoolingDownFeedback.FireUpdate(); +// }, CooldownTime); +// } +// IrPort.Pulse(IROutputStandardCommands.IROut_POWER_OFF, IrPulseTime); +// } + +// public override void PowerToggle() +// { +// // Not sure how to handle the feedback, but we should default to power off fb. +// // Does this need to trigger feedback?? +// _PowerIsOn = false; +// IrPort.Pulse(IROutputStandardCommands.IROut_POWER, IrPulseTime); +// } + +// #region IFunctionList Members + +// public List CueActionList +// { +// get; +// private set; +// } + +// #endregion + +// protected override Func PowerIsOnOutputFunc { get { return () => _PowerIsOn; } } +// protected override Func IsCoolingDownOutputFunc { get { return () => _IsCoolingDown; } } +// protected override Func IsWarmingUpOutputFunc { get { return () => _IsWarmingUp; } } + +// public override void ExecuteSwitch(object selector) +// { +// IrPort.Pulse((string)selector, IrPulseTime); +// } +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs new file mode 100644 index 00000000..9ef0ee86 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public abstract class DisplayBase : Device, IHasFeedback, IRoutingSinkWithSwitching, IPower, IWarmingCooling, IUsageTracking + { + public BoolFeedback PowerIsOnFeedback { get; protected set; } + public BoolFeedback IsCoolingDownFeedback { get; protected set; } + public BoolFeedback IsWarmingUpFeedback { get; private set; } + + public UsageTracking UsageTracker { get; set; } + + public uint WarmupTime { get; set; } + public uint CooldownTime { get; set; } + + /// + /// Bool Func that will provide a value for the PowerIsOn Output. Must be implemented + /// by concrete sub-classes + /// + abstract protected Func PowerIsOnFeedbackFunc { get; } + abstract protected Func IsCoolingDownFeedbackFunc { get; } + abstract protected Func IsWarmingUpFeedbackFunc { get; } + + + protected CTimer WarmupTimer; + protected CTimer CooldownTimer; + + #region IRoutingInputs Members + + public RoutingPortCollection InputPorts { get; private set; } + + #endregion + + public DisplayBase(string key, string name) + : base(key, name) + { + PowerIsOnFeedback = new BoolFeedback("PowerOnFeedback", PowerIsOnFeedbackFunc); + IsCoolingDownFeedback = new BoolFeedback("IsCoolingDown", IsCoolingDownFeedbackFunc); + IsWarmingUpFeedback = new BoolFeedback("IsWarmingUp", IsWarmingUpFeedbackFunc); + + InputPorts = new RoutingPortCollection(); + + PowerIsOnFeedback.OutputChange += PowerIsOnFeedback_OutputChange; + } + + void PowerIsOnFeedback_OutputChange(object sender, EventArgs e) + { + if (UsageTracker != null) + { + if (PowerIsOnFeedback.BoolValue) + UsageTracker.StartDeviceUsage(); + else + UsageTracker.EndDeviceUsage(); + } + } + + public abstract void PowerOn(); + public abstract void PowerOff(); + public abstract void PowerToggle(); + + public virtual FeedbackCollection Feedbacks + { + get + { + return new FeedbackCollection + { + PowerIsOnFeedback, + IsCoolingDownFeedback, + IsWarmingUpFeedback + }; + } + } + + public abstract void ExecuteSwitch(object selector); + + } + + /// + /// + /// + public abstract class TwoWayDisplayBase : DisplayBase + { + public StringFeedback CurrentInputFeedback { get; private set; } + + abstract protected Func CurrentInputFeedbackFunc { get; } + + + 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) + { + CurrentInputFeedback = new StringFeedback(CurrentInputFeedbackFunc); + + WarmupTime = 7000; + CooldownTime = 15000; + + Feedbacks.Add(CurrentInputFeedback); + + + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs new file mode 100644 index 00000000..82735d0f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/MockDisplay.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; + +using PepperDash.Core; +using PepperDash.Essentials.Core.Routing; + + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public class MockDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback + + { + 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 PowerIsOnFeedbackFunc { get { return () => _PowerIsOn; } } + protected override Func IsCoolingDownFeedbackFunc { get { return () => _IsCoolingDown; } } + protected override Func IsWarmingUpFeedbackFunc { get { return () => _IsWarmingUp; } } + protected override Func 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.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo, + 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); + } + + 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; + _PowerIsOn = false; + PowerIsOnFeedback.InvokeFireUpdate(); + IsCoolingDownFeedback.InvokeFireUpdate(); + // Fake cool-down cycle + CooldownTimer = new CTimer(o => + { + Debug.Console(2, this, "Cooldown timer ending"); + _IsCoolingDown = false; + IsCoolingDownFeedback.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.Console(2, this, "ExecuteSwitch: {0}", selector); + } + + + + #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.Console(2, 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.Console(2, 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 + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ethernet/EthernetStatistics.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ethernet/EthernetStatistics.cs new file mode 100644 index 00000000..13e49b5c --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ethernet/EthernetStatistics.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core.Ethernet +{ + public static class EthernetSettings + { + public static readonly BoolFeedback LinkActive = new BoolFeedback("LinkActive", + () => true); + public static readonly BoolFeedback DhcpActive = new BoolFeedback("DhcpActive", + () => CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, 0) == "ON"); + + + public static readonly StringFeedback Hostname = new StringFeedback("Hostname", + () => CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0)); + public static readonly StringFeedback IpAddress0 = new StringFeedback("IpAddress0", + () => CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)); + public static readonly StringFeedback SubnetMask0 = new StringFeedback("SubnetMask0", + () => CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0)); + public static readonly StringFeedback DefaultGateway0 = new StringFeedback("DefaultGateway0", + () => CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0)); + } + + public static class EthernetCue + { + public static readonly Cue LinkActive = Cue.BoolCue("LinkActive", 1); + public static readonly Cue DhcpActive = Cue.BoolCue("DhcpActive", 2); + + public static readonly Cue Hostname = Cue.StringCue("Hostname", 1); + public static readonly Cue IpAddress0 = Cue.StringCue("IpAddress0", 2); + public static readonly Cue SubnetMask0 = Cue.StringCue("SubnetMask0", 3); + public static readonly Cue DefaultGateway0 = Cue.StringCue("DefaultGateway0", 4); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs new file mode 100644 index 00000000..cfe5af0a --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.Core +{ + public class DeviceFactory + { + /// + /// A dictionary of factory methods, keyed by config types, added by plugins. + /// These methods are looked up and called by GetDevice in this class. + /// + static Dictionary> FactoryMethods = + new Dictionary>(StringComparer.OrdinalIgnoreCase); + + /// + /// Adds a plugin factory method + /// + /// + /// + public static void AddFactoryForType(string type, Func method) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", type); + DeviceFactory.FactoryMethods.Add(type, method); + } + + /// + /// The factory method for Core "things". Also iterates the Factory methods that have + /// been loaded from plugins + /// + /// + /// + public static IKeyed GetDevice(DeviceConfig dc) + { + var key = dc.Key; + var name = dc.Name; + var type = dc.Type; + var properties = dc.Properties; + + var typeName = dc.Type.ToLower(); + + // Check "core" types first + if (typeName == "genericcomm") + { + Debug.Console(1, "Factory Attempting to create new Generic Comm Device"); + return new GenericComm(dc); + } + + // then check for types that have been added by plugin dlls. + if (FactoryMethods.ContainsKey(typeName)) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading '{0}' from plugin", dc.Type); + return FactoryMethods[typeName](dc); + } + + return null; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs new file mode 100644 index 00000000..a5be1fb4 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedback.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +namespace PepperDash.Essentials.Core +{ + /// + /// A Feedback whose output is derived from the return value of a provided Func. + /// + public class BoolFeedback : Feedback + { + /// + /// Returns the current value of the feedback, derived from the ValueFunc. The ValueFunc is + /// evaluated whenever FireUpdate() is called + /// + public override bool BoolValue { get { return _BoolValue; } } + bool _BoolValue; + + public override eCueType Type { get { return eCueType.Bool; } } + + /// + /// Fake value to be used in test mode + /// + public bool TestValue { get; private set; } + + /// + /// Func that evaluates on FireUpdate + /// + public Func ValueFunc { get; private set; } + + List LinkedInputSigs = new List(); + List LinkedComplementInputSigs = new List(); + + public BoolFeedback(Func valueFunc) + : this(null, valueFunc) + { + } + + public BoolFeedback(string key, Func valueFunc) + : base(key) + { + ValueFunc = valueFunc; + } + + //public BoolFeedback(Cue cue, Func valueFunc) + // : base(cue) + //{ + // if (cue == null) throw new ArgumentNullException("cue"); + // ValueFunc = valueFunc; + //} + + public override void FireUpdate() + { + bool newValue = InTestMode ? TestValue : ValueFunc.Invoke(); + if (newValue != _BoolValue) + { + _BoolValue = newValue; + LinkedInputSigs.ForEach(s => UpdateSig(s)); + LinkedComplementInputSigs.ForEach(s => UpdateComplementSig(s)); + OnOutputChange(newValue); + } + } + + public void LinkInputSig(BoolInputSig sig) + { + LinkedInputSigs.Add(sig); + UpdateSig(sig); + } + + public void UnlinkInputSig(BoolInputSig sig) + { + LinkedInputSigs.Remove(sig); + } + + public void LinkComplementInputSig(BoolInputSig sig) + { + LinkedComplementInputSigs.Add(sig); + UpdateComplementSig(sig); + } + + public void UnlinkComplementInputSig(BoolInputSig sig) + { + LinkedComplementInputSigs.Remove(sig); + } + + public override string ToString() + { + return (InTestMode ? "TEST -- " : "") + BoolValue.ToString(); + } + + /// + /// Puts this in test mode, sets the test value and fires an update. + /// + /// + public void SetTestValue(bool value) + { + TestValue = value; + InTestMode = true; + FireUpdate(); + } + + void UpdateSig(BoolInputSig sig) + { + sig.BoolValue = _BoolValue; + } + + void UpdateComplementSig(BoolInputSig sig) + { + sig.BoolValue = !_BoolValue; + } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedbackOneShot.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedbackOneShot.cs new file mode 100644 index 00000000..cc183569 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedbackOneShot.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +namespace PepperDash.Essentials.Core +{ + public class BoolFeedbackPulse + { + public uint TimeoutMs { get; set; } + + /// + /// Defaults to false + /// + public bool CanRetrigger { get; set; } + + public BoolFeedback Feedback { get; private set; } + CTimer Timer; + + bool _BoolValue; + + /// + /// Creates a non-retriggering one shot + /// + public BoolFeedbackPulse(uint timeoutMs) + : this(timeoutMs, false) + { + } + + /// + /// Create a retriggerable one shot by setting canRetrigger true + /// + public BoolFeedbackPulse(uint timeoutMs, bool canRetrigger) + { + TimeoutMs = timeoutMs; + CanRetrigger = canRetrigger; + Feedback = new BoolFeedback(() => _BoolValue); + } + + /// + /// Starts the + /// + /// + public void Start() + { + if (Timer == null) + { + _BoolValue = true; + Feedback.FireUpdate(); + Timer = new CTimer(o => + { + _BoolValue = false; + Feedback.FireUpdate(); + Timer = null; + }, TimeoutMs); + } + // Timer is running, if retrigger is set, reset it. + else if (CanRetrigger) + Timer.Reset(TimeoutMs); + } + + public void Cancel() + { + if(Timer != null) + Timer.Reset(0); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedbackPulseExtender.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedbackPulseExtender.cs new file mode 100644 index 00000000..53a5e559 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolFeedbackPulseExtender.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +namespace PepperDash.Essentials.Core +{ + /// + /// A class that wraps a BoolFeedback with logic that extends it's true state for + /// a time period after the value goes false. + /// + public class BoolFeedbackPulseExtender + { + public uint TimeoutMs { get; set; } + public BoolFeedback Feedback { get; private set; } + CTimer Timer; + + /// + /// When set to true, will cause Feedback to go high, and cancel the timer. + /// When false, will start the timer, and after timeout, will go low and + /// feedback will go low. + /// + public bool BoolValue + { + get { return _BoolValue; } + set + { + if (value) + { // if Timer is running and the value goes high, cancel it. + if (Timer != null) + { + Timer.Stop(); + Timer = null; + } + // if it's already true, don't fire again + if (_BoolValue == true) + return; + _BoolValue = true; + Feedback.FireUpdate(); + } + else + { + if (Timer == null) + Timer = new CTimer(o => ClearFeedback(), TimeoutMs); + } + } + } + bool _BoolValue; + + /// + /// Constructor + /// + /// The time which the true state will be extended after set to false + public BoolFeedbackPulseExtender(uint timeoutMs) + { + TimeoutMs = timeoutMs; + Feedback = new BoolFeedback(() => this.BoolValue); + } + + /// + /// Forces the feedback to false regardless of timeout + /// + public void ClearNow() + { + if (Timer != null) + Timer.Stop(); + ClearFeedback(); + } + + void ClearFeedback() + { + _BoolValue = false; + Feedback.FireUpdate(); + Timer = null; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolOutputLogicals.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolOutputLogicals.cs new file mode 100644 index 00000000..3074254e --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/BoolOutputLogicals.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +namespace PepperDash.Essentials.Core +{ + + + public abstract class BoolFeedbackLogic + { + /// + /// Output representing the "and" value of all connected inputs + /// + public BoolFeedback Output { get; private set; } + + /// + /// List of all connected outputs + /// + protected List OutputsIn = new List(); + + protected bool ComputedValue; + + public BoolFeedbackLogic() + { + Output = new BoolFeedback(() => ComputedValue); + } + + public void AddOutputIn(BoolFeedback output) + { + // Don't double up outputs + if(OutputsIn.Contains(output)) return; + + OutputsIn.Add(output); + output.OutputChange += AnyInput_OutputChange; + Evaluate(); + } + + public void AddOutputsIn(List outputs) + { + foreach (var o in outputs) + { + // skip existing + if (OutputsIn.Contains(o)) continue; + + OutputsIn.Add(o); + o.OutputChange += AnyInput_OutputChange; + } + Evaluate(); + } + + public void RemoveOutputIn(BoolFeedback output) + { + // Don't double up outputs + if (OutputsIn.Contains(output)) return; + + OutputsIn.Remove(output); + output.OutputChange -= AnyInput_OutputChange; + Evaluate(); + } + + public void RemoveOutputsIn(List outputs) + { + foreach (var o in outputs) + { + OutputsIn.Remove(o); + o.OutputChange -= AnyInput_OutputChange; + } + Evaluate(); + } + + void AnyInput_OutputChange(object sender, EventArgs e) + { + Evaluate(); + } + + protected abstract void Evaluate(); + } + + public class BoolFeedbackAnd : BoolFeedbackLogic + { + protected override void Evaluate() + { + var prevValue = ComputedValue; + var newValue = OutputsIn.All(o => o.BoolValue); + if (newValue != prevValue) + { + ComputedValue = newValue; + Output.FireUpdate(); + } + } + } + + public class BoolFeedbackOr : BoolFeedbackLogic + { + protected override void Evaluate() + { + var prevValue = ComputedValue; + var newValue = OutputsIn.Any(o => o.BoolValue); + if (newValue != prevValue) + { + ComputedValue = newValue; + Output.FireUpdate(); + } + } + } + + public class BoolFeedbackLinq : BoolFeedbackLogic + { + Func, bool> Predicate; + + public BoolFeedbackLinq(Func, bool> predicate) + : base() + { + Predicate = predicate; + } + + protected override void Evaluate() + { + var prevValue = ComputedValue; + var newValue = Predicate(OutputsIn); + if (newValue != prevValue) + { + ComputedValue = newValue; + Output.FireUpdate(); + } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/FeedbackBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/FeedbackBase.cs new file mode 100644 index 00000000..96571dce --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/FeedbackBase.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public abstract class Feedback : IKeyed + { + public event EventHandler OutputChange; + + public string Key { get; private set; } + + public virtual bool BoolValue { get { return false; } } + public virtual int IntValue { get { return 0; } } + public virtual string StringValue { get { return ""; } } + public virtual string SerialValue { get { return ""; } } + + //public Cue Cue { get; private set; } + + public abstract eCueType Type { get; } + + /// + /// Feedbacks can be put into test mode for simulation of events without real data. + /// Using JSON debugging methods and the Set/ClearTestValue methods, we can simulate + /// Feedback behaviors + /// + public bool InTestMode { get; protected set; } + + /// + /// Base Constructor - empty + /// + protected Feedback() + { + } + + protected Feedback(string key) + { + if (key == null) + Key = ""; + else + Key = key; + } + + //protected Feedback(Cue cue) + //{ + // Cue = cue; + //} + + /// + /// Clears test mode and fires update. + /// + public void ClearTestValue() + { + InTestMode = false; + FireUpdate(); + } + + /// + /// Fires an update synchronously + /// + public abstract void FireUpdate(); + + /// + /// Fires the update asynchronously within a CrestronInvoke + /// + public void InvokeFireUpdate() + { + CrestronInvoke.BeginInvoke(o => FireUpdate()); + } + + ///// + ///// Helper method that fires event. Use this intstead of calling OutputChange + ///// + //protected void OnOutputChange() + //{ + // if (OutputChange != null) OutputChange(this, EventArgs.Empty); + //} + + protected void OnOutputChange(bool value) + { + if (OutputChange != null) OutputChange(this, new FeedbackEventArgs(value)); + } + + protected void OnOutputChange(int value) + { + if (OutputChange != null) OutputChange(this, new FeedbackEventArgs(value)); + } + + + protected void OnOutputChange(string value) + { + if (OutputChange != null) OutputChange(this, new FeedbackEventArgs(value)); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/FeedbackCollection.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/FeedbackCollection.cs new file mode 100644 index 00000000..ed7517da --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/FeedbackCollection.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Basically a List , with an indexer to find feedbacks by key name + /// + public class FeedbackCollection : List where T : Feedback + { + /// + /// Case-insensitive port lookup linked to feedbacks' keys + /// + public T this[string key] + { + get + { + return this.FirstOrDefault(i => i.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); + } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/FeedbackEventArgs.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/FeedbackEventArgs.cs new file mode 100644 index 00000000..9a7f5c29 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/FeedbackEventArgs.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + public class FeedbackEventArgs : EventArgs + { + public bool BoolValue { get; private set; } + public int IntValue { get; private set; } + public ushort UShortValue + { + get + { + return (ushort)IntValue; + } + } + public string StringValue { get; private set; } + public eFeedbackEventType Type { get; private set; } + + public FeedbackEventArgs(bool value) + { + BoolValue = value; + Type = eFeedbackEventType.TypeBool; + } + + public FeedbackEventArgs(int value) + { + IntValue = value; + Type = eFeedbackEventType.TypeInt; + } + + public FeedbackEventArgs(string value) + { + StringValue = value; + Type = eFeedbackEventType.TypeString; + } + } + + public enum eFeedbackEventType + { + TypeBool, + TypeInt, + TypeString + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs new file mode 100644 index 00000000..5591cb7f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/IntFeedback.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +namespace PepperDash.Essentials.Core +{ + public class IntFeedback : Feedback + { + public override int IntValue { get { return _IntValue; } } // ValueFunc.Invoke(); } } + int _IntValue; + public ushort UShortValue { get { return (ushort)_IntValue; } } + + public override eCueType Type { get { return eCueType.Int; } } + + public int TestValue { get; private set; } + + /// + /// Func evaluated on FireUpdate + /// + Func ValueFunc; + List LinkedInputSigs = new List(); + + public IntFeedback(Func valueFunc) + : this(null, valueFunc) + { + } + + public IntFeedback(string key, Func valueFunc) + : base(key) + { + ValueFunc = valueFunc; + } + + //public IntFeedback(Cue cue, Func valueFunc) + // : base(cue) + //{ + // if (cue == null) throw new ArgumentNullException("cue"); + // ValueFunc = valueFunc; + //} + + public override void FireUpdate() + { + var newValue = InTestMode ? TestValue : ValueFunc.Invoke(); + if (newValue != _IntValue) + { + _IntValue = newValue; + LinkedInputSigs.ForEach(s => UpdateSig(s)); + OnOutputChange(newValue); + } + } + + public void LinkInputSig(UShortInputSig sig) + { + LinkedInputSigs.Add(sig); + UpdateSig(sig); + } + + public void UnlinkInputSig(UShortInputSig sig) + { + LinkedInputSigs.Remove(sig); + } + + public override string ToString() + { + return (InTestMode ? "TEST -- " : "") + IntValue.ToString(); + } + + /// + /// Puts this in test mode, sets the test value and fires an update. + /// + /// + public void SetTestValue(int value) + { + TestValue = value; + InTestMode = true; + FireUpdate(); + } + + void UpdateSig(UShortInputSig sig) + { + sig.UShortValue = UShortValue; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/SerialFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/SerialFeedback.cs new file mode 100644 index 00000000..0e6ed424 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/SerialFeedback.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +namespace PepperDash.Essentials.Core +{ + /// + /// To be used for serial data feedback where the event chain / asynchronicity must be maintained + /// and calculating the value based on a Func when it is needed will not suffice. + /// + public class SerialFeedback : Feedback + { + public override string SerialValue { get { return _SerialValue; } } + string _SerialValue; + + public override eCueType Type { get { return eCueType.Serial; } } + + /// + /// Used in testing. Set/Clear functions + /// + public string TestValue { get; private set; } + + List LinkedInputSigs = new List(); + + public SerialFeedback() + { + } + + public SerialFeedback(string key) + : base(key) + { + } + + public override void FireUpdate() + { + throw new NotImplementedException("This feedback type does not use Funcs"); + } + + public void FireUpdate(string newValue) + { + _SerialValue = newValue; + LinkedInputSigs.ForEach(s => UpdateSig(s, newValue)); + OnOutputChange(newValue); + } + + public void LinkInputSig(StringInputSig sig) + { + LinkedInputSigs.Add(sig); + UpdateSig(sig); + } + + public void UnlinkInputSig(StringInputSig sig) + { + LinkedInputSigs.Remove(sig); + } + + public override string ToString() + { + return (InTestMode ? "TEST -- " : "") + SerialValue; + } + + /// + /// Puts this in test mode, sets the test value and fires an update. + /// + /// + public void SetTestValue(string value) + { + TestValue = value; + InTestMode = true; + FireUpdate(TestValue); + } + + void UpdateSig(StringInputSig sig) + { + sig.StringValue = _SerialValue; + } + + void UpdateSig(StringInputSig sig, string value) + { + sig.StringValue = value; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs new file mode 100644 index 00000000..e4cd7084 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Feedbacks/StringFeedback.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +namespace PepperDash.Essentials.Core +{ + public class StringFeedback : Feedback + { + public override string StringValue { get { return _StringValue; } } // ValueFunc.Invoke(); } } + string _StringValue; + + public override eCueType Type { get { return eCueType.String; } } + + /// + /// Used in testing. Set/Clear functions + /// + public string TestValue { get; private set; } + + /// + /// Evalutated on FireUpdate + /// + public Func ValueFunc { get; private set; } + List LinkedInputSigs = new List(); + + public StringFeedback(Func valueFunc) + : this(null, valueFunc) + { + } + + public StringFeedback(string key, Func valueFunc) + : base(key) + { + ValueFunc = valueFunc; + } + + //public StringFeedback(Cue cue, Func valueFunc) + // : base(cue) + //{ + // if (cue == null) throw new ArgumentNullException("cue"); + // ValueFunc = valueFunc; + + //} + + public override void FireUpdate() + { + var newValue = InTestMode ? TestValue : ValueFunc.Invoke(); + if (newValue != _StringValue) + { + _StringValue = newValue; + LinkedInputSigs.ForEach(s => UpdateSig(s)); + OnOutputChange(newValue); + } + } + + public void LinkInputSig(StringInputSig sig) + { + LinkedInputSigs.Add(sig); + UpdateSig(sig); + } + + public void UnlinkInputSig(StringInputSig sig) + { + LinkedInputSigs.Remove(sig); + } + + public override string ToString() + { + return (InTestMode ? "TEST -- " : "") + StringValue; + } + + /// + /// Puts this in test mode, sets the test value and fires an update. + /// + /// + public void SetTestValue(string value) + { + TestValue = value; + InTestMode = true; + FireUpdate(); + } + + void UpdateSig(StringInputSig sig) + { + sig.StringValue = _StringValue; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/MOVED FusionSystemController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/MOVED FusionSystemController.cs new file mode 100644 index 00000000..8e7e70c6 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/MOVED FusionSystemController.cs @@ -0,0 +1,377 @@ +//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.Fusion; +//using PepperDash.Essentials.Core; + +//using PepperDash.Core; + + +//namespace PepperDash.Essentials.Core.Fusion +//{ +// public class EssentialsHuddleSpaceFusionSystemController : Device +// { +// FusionRoom FusionRoom; +// Room Room; +// Dictionary SourceToFeedbackSigs = new Dictionary(); + +// StatusMonitorCollection ErrorMessageRollUp; + +// public EssentialsHuddleSpaceFusionSystemController(HuddleSpaceRoom room, uint ipId) +// : base(room.Key + "-fusion") +// { +// Room = room; + +// FusionRoom = new FusionRoom(ipId, Global.ControlSystem, room.Name, "awesomeGuid-" + room.Key); +// FusionRoom.Register(); + +// FusionRoom.FusionStateChange += new FusionStateEventHandler(FusionRoom_FusionStateChange); + +// // Room to fusion room +// room.RoomIsOn.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); +// var srcName = FusionRoom.CreateOffsetStringSig(50, "Source - Name", eSigIoMask.InputSigOnly); +// room.CurrentSourceName.LinkInputSig(srcName.InputSig); + +// FusionRoom.SystemPowerOn.OutputSig.UserObject = new Action(b => { if (b) room.RoomOn(null); }); +// FusionRoom.SystemPowerOff.OutputSig.UserObject = new Action(b => { if (b) room.RoomOff(); }); +// // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); +// FusionRoom.ErrorMessage.InputSig.StringValue = "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;"; + +// // Sources +// foreach (var src in room.Sources) +// { +// var srcNum = src.Key; +// var pSrc = src.Value as IPresentationSource; +// var keyNum = ExtractNumberFromKey(pSrc.Key); +// if (keyNum == -1) +// { +// Debug.Console(1, this, "WARNING: Cannot link source '{0}' to numbered Fusion attributes", pSrc.Key); +// continue; +// } +// string attrName = null; +// uint attrNum = Convert.ToUInt32(keyNum); +// switch (pSrc.Type) +// { +// case PresentationSourceType.None: +// break; +// case PresentationSourceType.SetTopBox: +// attrName = "Source - TV " + keyNum; +// attrNum += 115; // TV starts at 116 +// break; +// case PresentationSourceType.Dvd: +// attrName = "Source - DVD " + keyNum; +// attrNum += 120; // DVD starts at 121 +// break; +// case PresentationSourceType.PC: +// attrName = "Source - PC " + keyNum; +// attrNum += 110; // PC starts at 111 +// break; +// case PresentationSourceType.Laptop: +// attrName = "Source - Laptop " + keyNum; +// attrNum += 100; // Laptops start at 101 +// break; +// case PresentationSourceType.VCR: +// attrName = "Source - VCR " + keyNum; +// attrNum += 125; // VCRs start at 126 +// break; +// } +// if (attrName == null) +// { +// Debug.Console(1, this, "Source type {0} does not have corresponsing Fusion attribute type, skipping", pSrc.Type); +// continue; +// } +// Debug.Console(2, this, "Creating attribute '{0}' with join {1} for source {2}", attrName, attrNum, pSrc.Key); +// var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig); +// // Need feedback when this source is selected +// // Event handler, added below, will compare source changes with this sig dict +// SourceToFeedbackSigs.Add(pSrc, sigD.InputSig); + +// // And respond to selection in Fusion +// sigD.OutputSig.UserObject = new Action(b => { if(b) room.SelectSource(pSrc); }); +// } + +// // Attach to all room's devices with monitors. +// //foreach (var dev in DeviceManager.Devices) +// foreach (var dev in DeviceManager.GetDevices()) +// { +// if (!(dev is ICommunicationMonitor)) +// continue; + +// var keyNum = ExtractNumberFromKey(dev.Key); +// if (keyNum == -1) +// { +// Debug.Console(1, this, "WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes", dev.Key); +// continue; +// } +// string attrName = null; +// uint attrNum = Convert.ToUInt32(keyNum); + +// //if (dev is SmartGraphicsTouchpanelControllerBase) +// //{ +// // if (attrNum > 10) +// // continue; +// // attrName = "Device Ok - Touch Panel " + attrNum; +// // attrNum += 200; +// //} +// //// add xpanel here + +// //else +// if (dev is DisplayBase) +// { +// if (attrNum > 10) +// continue; +// attrName = "Device Ok - Display " + attrNum; +// attrNum += 240; +// } +// //else if (dev is DvdDeviceBase) +// //{ +// // if (attrNum > 5) +// // continue; +// // attrName = "Device Ok - DVD " + attrNum; +// // attrNum += 260; +// //} +// // add set top box + +// // add Cresnet roll-up + +// // add DM-devices roll-up + +// if (attrName != null) +// { +// // Link comm status to sig and update +// var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly); +// var smd = dev as ICommunicationMonitor; +// sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk; +// smd.CommunicationMonitor.StatusChange += (o, a) => { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; }; +// Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", dev.Key, attrName); +// } +// } + +// // Don't think we need to get current status of this as nothing should be alive yet. +// room.PresentationSourceChange += Room_PresentationSourceChange; + +// // these get used in multiple places +// var display = room.Display; +// var dispPowerOnAction = new Action(b => { if (!b) display.PowerOn(); }); +// var dispPowerOffAction = new Action(b => { if (!b) display.PowerOff(); }); + +// // Display to fusion room sigs +// FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; +// FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; +// display.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); +// if (display is IDisplayUsage) +// (display as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); + +// // Roll up ALL device errors +// ErrorMessageRollUp = new StatusMonitorCollection(this); +// foreach (var dev in DeviceManager.GetDevices()) +// { +// var md = dev as ICommunicationMonitor; +// if (md != null) +// { +// ErrorMessageRollUp.AddMonitor(md.CommunicationMonitor); +// Debug.Console(2, this, "Adding '{0}' to room's overall error monitor", md.CommunicationMonitor.Parent.Key); +// } +// } +// ErrorMessageRollUp.Start(); +// FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message; +// ErrorMessageRollUp.StatusChange += (o, a) => { +// FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message; }; + + +// // static assets --------------- testing + +// // test assets --- THESE ARE BOTH WIRED TO AssetUsage somewhere internally. +// var ta1 = FusionRoom.CreateStaticAsset(1, "Test asset 1", "Awesome Asset", "Awesome123"); +// ta1.AssetError.InputSig.StringValue = "This should be error"; + + +// var ta2 = FusionRoom.CreateStaticAsset(2, "Test asset 2", "Awesome Asset", "Awesome1232"); +// ta2.AssetUsage.InputSig.StringValue = "This should be usage"; + + +// // Make a display asset +// var dispAsset = FusionRoom.CreateStaticAsset(3, display.Name, "Display", "awesomeDisplayId" + room.Key); +// dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; +// dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; +// display.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); +// // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); +// // Use extension methods +// dispAsset.TrySetMakeModel(display); +// dispAsset.TryLinkAssetErrorToCommunication(display); + + +// // Make it so! +// FusionRVI.GenerateFileForAllFusionDevices(); +// } + +// /// +// /// Helper to get the number from the end of a device's key string +// /// +// /// -1 if no number matched +// int ExtractNumberFromKey(string key) +// { +// var capture = System.Text.RegularExpressions.Regex.Match(key, @"\D+(\d+)"); +// if (!capture.Success) +// return -1; +// else return Convert.ToInt32(capture.Groups[1].Value); +// } + +// void Room_PresentationSourceChange(object sender, EssentialsRoomSourceChangeEventArgs e) +// { +// if (e.OldSource != null) +// { +// if (SourceToFeedbackSigs.ContainsKey(e.OldSource)) +// SourceToFeedbackSigs[e.OldSource].BoolValue = false; +// } +// if (e.NewSource != null) +// { +// if (SourceToFeedbackSigs.ContainsKey(e.NewSource)) +// SourceToFeedbackSigs[e.NewSource].BoolValue = true; +// } +// } + +// void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args) +// { + +// // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors, +// // even though they all contain sigs. + +// var sigData = (args.UserConfiguredSigDetail as BooleanSigDataFixedName); +// if (sigData != null) +// { +// var outSig = sigData.OutputSig; +// if (outSig.UserObject is Action) +// (outSig.UserObject as Action).Invoke(outSig.BoolValue); +// else if (outSig.UserObject is Action) +// (outSig.UserObject as Action).Invoke(outSig.UShortValue); +// else if (outSig.UserObject is Action) +// (outSig.UserObject as Action).Invoke(outSig.StringValue); +// return; +// } + +// var attrData = (args.UserConfiguredSigDetail as BooleanSigData); +// if (attrData != null) +// { +// var outSig = attrData.OutputSig; +// if (outSig.UserObject is Action) +// (outSig.UserObject as Action).Invoke(outSig.BoolValue); +// else if (outSig.UserObject is Action) +// (outSig.UserObject as Action).Invoke(outSig.UShortValue); +// else if (outSig.UserObject is Action) +// (outSig.UserObject as Action).Invoke(outSig.StringValue); +// return; +// } + +// } +// } + + +// public static class FusionRoomExtensions +// { +// /// +// /// Creates and returns a fusion attribute. The join number will match the established Simpl +// /// standard of 50+, and will generate a 50+ join in the RVI. It calls +// /// FusionRoom.AddSig with join number - 49 +// /// +// /// The new attribute +// public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) +// { +// if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); +// number -= 49; +// fr.AddSig(eSigType.Bool, number, name, mask); +// return fr.UserDefinedBooleanSigDetails[number]; +// } + +// /// +// /// Creates and returns a fusion attribute. The join number will match the established Simpl +// /// standard of 50+, and will generate a 50+ join in the RVI. It calls +// /// FusionRoom.AddSig with join number - 49 +// /// +// /// The new attribute +// public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) +// { +// if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); +// number -= 49; +// fr.AddSig(eSigType.UShort, number, name, mask); +// return fr.UserDefinedUShortSigDetails[number]; +// } + +// /// +// /// Creates and returns a fusion attribute. The join number will match the established Simpl +// /// standard of 50+, and will generate a 50+ join in the RVI. It calls +// /// FusionRoom.AddSig with join number - 49 +// /// +// /// The new attribute +// public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) +// { +// if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); +// number -= 49; +// fr.AddSig(eSigType.String, number, name, mask); +// return fr.UserDefinedStringSigDetails[number]; +// } + +// /// +// /// Creates and returns a static asset +// /// +// /// the new asset +// public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type, string instanceId) +// { +// fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId); +// return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset; +// } +// } + +// //************************************************************************************************ +// /// +// /// Extensions to enhance Fusion room, asset and signal creation. +// /// +// public static class FusionStaticAssetExtensions +// { +// /// +// /// Tries to set a Fusion asset with the make and model of a device. +// /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset. +// /// Otherwise, does nothing. +// /// +// public static void TrySetMakeModel(this FusionStaticAsset asset, Device device) +// { +// var mm = device as IMakeModel; +// if (mm != null) +// { +// asset.ParamMake.Value = mm.DeviceMake; +// asset.ParamModel.Value = mm.DeviceModel; +// } +// } + +// /// +// /// Tries to attach the AssetError input on a Fusion asset to a Device's +// /// CommunicationMonitor.StatusChange event. Does nothing if the device is not +// /// IStatusMonitor +// /// +// /// +// /// +// public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device) +// { +// if (device is ICommunicationMonitor) +// { +// var monitor = (device as ICommunicationMonitor).CommunicationMonitor; +// monitor.StatusChange += (o, a) => +// { +// // Link connected and error inputs on asset +// asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; +// asset.AssetError.InputSig.StringValue = a.Status.ToString(); +// }; +// // set current value +// asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk; +// asset.AssetError.InputSig.StringValue = monitor.Status.ToString(); +// } +// } +// } + +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs new file mode 100644 index 00000000..6ce3446e --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs @@ -0,0 +1,60 @@ +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronDataStore; +using Crestron.SimplSharpPro; + +//using PepperDash.Essentials.Core.Http; +using PepperDash.Essentials.License; + + + +namespace PepperDash.Essentials.Core +{ + public static class Global + { + public static CrestronControlSystem ControlSystem { get; set; } + + public static LicenseManager LicenseManager { get; set; } + + /// + /// The file path prefix to the folder containing configuration files + /// + public static string FilePathPrefix { get; private set; } + + /// + /// Returns the directory separator character based on the running OS + /// + public static char DirectorySeparator + { + get + { + return System.IO.Path.DirectorySeparatorChar; + } + } + + /// + /// Wildcarded config file name for global reference + /// + public const string ConfigFileName = "*configurationFile*.json"; + + /// + /// Sets the file path prefix + /// + /// + public static void SetFilePathPrefix(string prefix) + { + FilePathPrefix = prefix; + } + + static Global() + { + // Fire up CrestronDataStoreStatic + var err = CrestronDataStoreStatic.InitCrestronDataStore(); + if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + { + CrestronConsole.PrintLine("Error starting CrestronDataStoreStatic: {0}", err); + return; + } + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/JobTimer.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/JobTimer.cs new file mode 100644 index 00000000..83159c12 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/JobTimer.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + public static class JobTimer + { + static CTimer MinuteTimer; + + static List Items = new List(); + + /// + /// + /// + /// + public static void AddAction(Action act) + { + + } + + /// + /// + /// + /// + /// + public static void AddJobTimerItem(JobTimerItem item) + { + var existing = Items.FirstOrDefault(i => i.Key == item.Key); + if (existing != null) + { + Items.Remove(existing); + } + Items.Add(item); + } + + static void CheckAndRunTimer() + { + if (Items.Count > 0 && MinuteTimer == null) + { + MinuteTimer = new CTimer(o => MinuteTimerCallback(), null, 60000, 60000); + } + } + + static void MinuteTimerCallback() + { + + + } + } + + /// + /// + /// + public class JobTimerItem + { + public string Key { get; private set; } + public Action JobAction { get; private set; } + public eJobTimerCycleTypes CycleType { get; private set; } + /// + /// + /// + public DateTime RunNextAt { get; set; } + + public JobTimerItem(string key, eJobTimerCycleTypes cycle, Action act) + { + + } + } + + public enum eJobTimerCycleTypes + { + RunEveryDay, + RunEveryHour, + RunEveryHalfHour, + RunEveryMinute + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs new file mode 100644 index 00000000..a7b721ef --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Scheduler; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// Global Scheduler for the system + /// + public static class Scheduler + { + private static Dictionary EventGroups = new Dictionary(); + + static Scheduler() + { + CrestronConsole.AddNewConsoleCommand(ClearEventsFromGroup, "ClearAllEvents", "Clears all scheduled events for this group", ConsoleAccessLevelEnum.AccessOperator); + + CrestronConsole.AddNewConsoleCommand(ListAllEventGroups, "ListAllEventGroups", "Lists all the event groups by key", ConsoleAccessLevelEnum.AccessOperator); + } + + /// + /// Clears (deletes) all events from a group + /// + /// + static void ClearEventsFromGroup(string groupName) + { + var group = EventGroups[groupName]; + + if (group != null) + group.ClearAllEvents(); + else + Debug.Console(0, "[Scheduler]: Unable to delete events from group '{0}'. Group not found in EventGroups dictionary.", groupName); + } + + static void ListAllEventGroups(string command) + { + Debug.Console(0, "Event Groups:"); + foreach (var group in EventGroups) + { + Debug.Console(0, "{0}", group.Key); + } + } + + /// + /// Adds the event group to the global list + /// + /// + /// + public static void AddEventGroup(ScheduledEventGroup eventGroup) + { + // Add this group to the global collection + if (!EventGroups.ContainsKey(eventGroup.Name)) + EventGroups.Add(eventGroup.Name, eventGroup); + } + + /// + /// Removes the event group from the global list + /// + /// + public static void RemoveEventGroup(ScheduledEventGroup eventGroup) + { + if(!EventGroups.ContainsKey(eventGroup.Name)) + EventGroups.Remove(eventGroup.Name); + } + } + + public static class SchedulerUtilities + { + /// + /// Checks the day of week in eventTime to see if it matches the weekdays defined in the recurrence enum. + /// + /// + /// + /// + public static bool CheckIfDayOfWeekMatchesRecurrenceDays(DateTime eventTime, ScheduledEventCommon.eWeekDays recurrence) + { + bool isMatch = false; + + var dayOfWeek = eventTime.DayOfWeek; + + Debug.Console(1, "[Scheduler]: eventTime day of week is: {0}", dayOfWeek); + + switch (dayOfWeek) + { + case DayOfWeek.Sunday: + { + if ((recurrence & ScheduledEventCommon.eWeekDays.Sunday) == ScheduledEventCommon.eWeekDays.Sunday) + isMatch = true; + break; + } + case DayOfWeek.Monday: + { + if ((recurrence & ScheduledEventCommon.eWeekDays.Monday) == ScheduledEventCommon.eWeekDays.Monday) + isMatch = true; + break; + } + case DayOfWeek.Tuesday: + { + if ((recurrence & ScheduledEventCommon.eWeekDays.Tuesday) == ScheduledEventCommon.eWeekDays.Tuesday) + isMatch = true; + break; + } + case DayOfWeek.Wednesday: + { + if ((recurrence & ScheduledEventCommon.eWeekDays.Wednesday) == ScheduledEventCommon.eWeekDays.Wednesday) + isMatch = true; + break; + } + case DayOfWeek.Thursday: + { + if ((recurrence & ScheduledEventCommon.eWeekDays.Thursday) == ScheduledEventCommon.eWeekDays.Thursday) + isMatch = true; + break; + } + case DayOfWeek.Friday: + { + if ((recurrence & ScheduledEventCommon.eWeekDays.Friday) == ScheduledEventCommon.eWeekDays.Friday) + isMatch = true; + break; + } + case DayOfWeek.Saturday: + { + if ((recurrence & ScheduledEventCommon.eWeekDays.Saturday) == ScheduledEventCommon.eWeekDays.Saturday) + isMatch = true; + break; + } + } + + Debug.Console(1, "[Scheduler]: eventTime day of week matches recurrence days: {0}", isMatch); + + return isMatch; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/InUseTracking/IInUseTracking.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/InUseTracking/IInUseTracking.cs new file mode 100644 index 00000000..97a7c7f6 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/InUseTracking/IInUseTracking.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Defines a class that uses an InUseTracker + /// + public interface IInUseTracking + { + InUseTracking InUseTracker { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/InUseTracking/InUseTracking.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/InUseTracking/InUseTracking.cs new file mode 100644 index 00000000..4bf1a551 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/InUseTracking/InUseTracking.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + /// + /// Provides in use tracking. Objects can register with this. InUseFeedback can provide + /// events when usage changes. + /// + public class InUseTracking + { + /// + /// Returns a copied list of all users of this tracker. + /// + public IEnumerable Users { get { return new List(_Users); } } + List _Users = new List(); + + /// + /// Feedback that changes when this goes in/out of use + /// + public BoolFeedback InUseFeedback { get; private set; } + + /// + /// Feedback that changes with the count of users + /// + public IntFeedback InUseCountFeedback { get; private set; } + + public InUseTracking() + { + InUseFeedback = new BoolFeedback(() => _Users.Count > 0); + InUseCountFeedback = new IntFeedback(() => _Users.Count); + } + + /// + /// Add a "user" object to this tracker. A user can be added to this tracker + /// multiple times, provided that the label is different + /// + /// A label to identify the instance of the user. Treated like a "role", etc. + public void AddUser(object objectToAdd, string label) + { + // check if an exact object/label pair exists and ignore if so. No double-registers. + var check = _Users.FirstOrDefault(u => u.Label == label && u.User == objectToAdd); + if (check != null) return; + + var prevCount = _Users.Count; + _Users.Add(new InUseTrackingObject(objectToAdd, label)); + // if this is the first add, fire an update + if (prevCount == 0 && _Users.Count > 0) + InUseFeedback.FireUpdate(); + InUseCountFeedback.FireUpdate(); + } + + /// + /// Remove a user object from this tracking + /// + public void RemoveUser(object objectToRemove, string label) + { + // Find the user object if exists and remove it + var toRemove = _Users.FirstOrDefault(u => u.Label == label && u.User == objectToRemove); + if (toRemove != null) + { + _Users.Remove(toRemove); + if (_Users.Count == 0) + InUseFeedback.FireUpdate(); + InUseCountFeedback.FireUpdate(); + } + } + } + + /// + /// Wrapper for label/object pair representing in-use status. Allows the same object to + /// register for in-use with different roles. + /// + public class InUseTrackingObject + { + public string Label { get; private set; } + public object User { get; private set; } + + public InUseTrackingObject(object user, string label) + { + User = user; + Label = label; + } + } + + //public class InUseEventArgs + //{ + // public int EventType { get; private set; } + // public InUseTracking Tracker { get; private set; } + + // public InUseEventArgs(InUseTracking tracker, int eventType) + // { + // Tracker = tracker; + // EventType = eventType; + // } + //} +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/License/EssentialsLicenseManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/License/EssentialsLicenseManager.cs new file mode 100644 index 00000000..c5f2d5e9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/License/EssentialsLicenseManager.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronDataStore; + +using PepperDash.Essentials.Core; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.License +{ + public abstract class LicenseManager + { + public BoolFeedback LicenseIsValid { get; protected set; } + public StringFeedback LicenseMessage { get; protected set; } + public StringFeedback LicenseLog { get; protected set; } + + protected LicenseManager() + { + CrestronConsole.AddNewConsoleCommand( + s => CrestronConsole.ConsoleCommandResponse(GetStatusString()), + "licensestatus", "shows license and related data", + ConsoleAccessLevelEnum.AccessOperator); + } + + protected abstract string GetStatusString(); + } + + public class MockEssentialsLicenseManager : LicenseManager + { + /// + /// Returns the singleton mock license manager for this app + /// + public static MockEssentialsLicenseManager Manager + { + get + { + if (_Manager == null) + _Manager = new MockEssentialsLicenseManager(); + return _Manager; + } + } + static MockEssentialsLicenseManager _Manager; + + bool IsValid; + + MockEssentialsLicenseManager() : base() + { + LicenseIsValid = new BoolFeedback("LicenseIsValid", + () => { return IsValid; }); + CrestronConsole.AddNewConsoleCommand( + s => SetFromConsole(s.Equals("true", StringComparison.OrdinalIgnoreCase)), + "mocklicense", "true or false for testing", ConsoleAccessLevelEnum.AccessOperator); + + bool valid; + var err = CrestronDataStoreStatic.GetGlobalBoolValue("MockLicense", out valid); + if (err == CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + SetIsValid(valid); + else if (err == CrestronDataStore.CDS_ERROR.CDS_RECORD_NOT_FOUND) + CrestronDataStoreStatic.SetGlobalBoolValue("MockLicense", false); + else + CrestronConsole.PrintLine("Error restoring Mock License setting: {0}", err); + } + + void SetIsValid(bool isValid) + { + IsValid = isValid; + CrestronDataStoreStatic.SetGlobalBoolValue("MockLicense", isValid); + Debug.Console(0, "Mock License is{0} valid", IsValid ? "" : " not"); + LicenseIsValid.FireUpdate(); + } + + void SetFromConsole(bool isValid) + { + SetIsValid(isValid); + } + + protected override string GetStatusString() + { + return string.Format("License Status: {0}", IsValid ? "Valid" : "Not Valid"); + } + } + + public class EssentialsLicenseManager + { + + } + + public class LicenseCue + { + public static Cue LicenseIsValid = Cue.BoolCue("LicenseIsValid", 15991); + + public static Cue LicenseMessage = Cue.StringCue("LicenseMessage", 15991); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Lighting/Lighting Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Lighting/Lighting Interfaces.cs new file mode 100644 index 00000000..6c0eaad3 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Lighting/Lighting Interfaces.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.Lighting +{ + /// + /// Requirements for a device that implements lighting scene control + /// + public interface ILightingScenes + { + event EventHandler LightingSceneChange; + + List LightingScenes { get; } + + void SelectScene(LightingScene scene); + + LightingScene CurrentLightingScene { get; } + + } + + /// + /// Requirements for a device that implements master raise/lower + /// + public interface ILightingMasterRaiseLower + { + void MasterRaise(); + void MasterLower(); + void MasterRaiseLowerStop(); + } + + /// + /// Requiremnts for controlling a lighting load + /// + public interface ILightingLoad + { + void SetLoadLevel(int level); + void Raise(); + void Lower(); + + IntFeedback LoadLevelFeedback { get; } + BoolFeedback LoadIsOnFeedback { get; } + } + + public class LightingSceneChangeEventArgs : EventArgs + { + public LightingScene CurrentLightingScene { get; private set; } + + public LightingSceneChangeEventArgs(LightingScene scene) + { + CurrentLightingScene = scene; + } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Lighting/LightingBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Lighting/LightingBase.cs new file mode 100644 index 00000000..af779c16 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Lighting/LightingBase.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.Lighting +{ + public abstract class LightingBase : Device, ILightingScenes + { + #region ILightingScenes Members + + public event EventHandler LightingSceneChange; + + public List LightingScenes { get; protected set; } + + public LightingScene CurrentLightingScene { get; protected set; } + + public IntFeedback CurrentLightingSceneFeedback { get; protected set; } + + #endregion + + + public LightingBase(string key, string name) + : base(key, name) + { + LightingScenes = new List(); + + CurrentLightingScene = new LightingScene(); + //CurrentLightingSceneFeedback = new IntFeedback(() => { return int.Parse(this.CurrentLightingScene.ID); }); + } + + public abstract void SelectScene(LightingScene scene); + + public void SimulateSceneSelect(string sceneName) + { + Debug.Console(1, this, "Simulating selection of scene '{0}'", sceneName); + + var scene = LightingScenes.FirstOrDefault(s => s.Name.Equals(sceneName)); + + if (scene != null) + { + CurrentLightingScene = scene; + OnLightingSceneChange(); + } + } + + /// + /// Sets the IsActive property on each scene and fires the LightingSceneChange event + /// + protected void OnLightingSceneChange() + { + foreach (var scene in LightingScenes) + { + if (scene == CurrentLightingScene) + scene.IsActive = true; + + else + scene.IsActive = false; + } + + var handler = LightingSceneChange; + if (handler != null) + { + handler(this, new LightingSceneChangeEventArgs(CurrentLightingScene)); + } + } + + } + + public class LightingScene + { + public string Name { get; set; } + public string ID { get; set; } + bool _IsActive; + public bool IsActive + { + get + { + return _IsActive; + } + set + { + _IsActive = value; + IsActiveFeedback.FireUpdate(); + } + } + public BoolFeedback IsActiveFeedback { get; set; } + + public LightingScene() + { + IsActiveFeedback = new BoolFeedback(new Func(() => IsActive)); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/CrestronGenericBaseCommunicationMonitor.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/CrestronGenericBaseCommunicationMonitor.cs new file mode 100644 index 00000000..bd57b70a --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/CrestronGenericBaseCommunicationMonitor.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using System.ComponentModel; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public class CrestronGenericBaseCommunicationMonitor : StatusMonitorBase + { + GenericBase Device; + + public CrestronGenericBaseCommunicationMonitor(IKeyed parent, GenericBase device, long warningTime, long errorTime) + : base(parent, warningTime, errorTime) + { + Device = device; + } + + public override void Start() + { + Device.OnlineStatusChange -= Device_OnlineStatusChange; + Device.OnlineStatusChange += Device_OnlineStatusChange; + GetStatus(); + } + + public override void Stop() + { + Device.OnlineStatusChange -= Device_OnlineStatusChange; + } + + void Device_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) + { + GetStatus(); + } + + void GetStatus() + { + if (Device.IsOnline) + { + Status = MonitorStatus.IsOk; + StopErrorTimers(); + } + else + StartErrorTimers(); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/GenericCommunicationMonitor.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/GenericCommunicationMonitor.cs new file mode 100644 index 00000000..0d19762f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/GenericCommunicationMonitor.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using System.ComponentModel; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + /// + /// Used for monitoring comms that are IBasicCommunication. Will send a poll string and provide an event when + /// statuses change. + /// + public class GenericCommunicationMonitor : StatusMonitorBase + { + public IBasicCommunication Client { get; private set; } + + long PollTime; + CTimer PollTimer; + string PollString; + Action PollAction; + + /// + /// + /// + /// + /// in MS, >= 5000 + /// in MS, >= 5000 + /// in MS, >= 5000 + /// String to send to comm + public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime, + long warningTime, long errorTime, string pollString) : + base(parent, warningTime, errorTime) + { + if (pollTime > warningTime || pollTime > errorTime) + throw new ArgumentException("pollTime must be less than warning or errorTime"); + //if (pollTime < 5000) + // throw new ArgumentException("pollTime cannot be less than 5000 ms"); + + Client = client; + PollTime = pollTime; + PollString = pollString; + } + + /// + /// Poll is a provided action instead of string + /// + /// + /// + /// + /// + /// + /// + public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, long pollTime, + long warningTime, long errorTime, Action pollAction) : + base(parent, warningTime, errorTime) + { + if (pollTime > warningTime || pollTime > errorTime) + throw new ArgumentException("pollTime must be less than warning or errorTime"); + //if (pollTime < 5000) + // throw new ArgumentException("pollTime cannot be less than 5000 ms"); + + Client = client; + PollTime = pollTime; + PollAction = pollAction; + } + + + /// + /// Build the monitor from a config object + /// + public GenericCommunicationMonitor(IKeyed parent, IBasicCommunication client, + CommunicationMonitorConfig props) : + this(parent, client, props.PollInterval, props.TimeToWarning, props.TimeToError, props.PollString) + { + } + + public override void Start() + { + Client.BytesReceived += Client_BytesReceived; + Poll(); + PollTimer = new CTimer(o => Poll(), null, PollTime, PollTime); + } + + public override void Stop() + { + Client.BytesReceived -= this.Client_BytesReceived; + PollTimer.Stop(); + PollTimer = null; + StopErrorTimers(); + } + + /// + /// Upon any receipt of data, set everything to ok! + /// + /// + /// + void Client_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs e) + { + Status = MonitorStatus.IsOk; + ResetErrorTimers(); + // + } + + void Poll() + { + StartErrorTimers(); + if (Client.IsConnected) + { + //Debug.Console(2, this, "Polling"); + if(PollAction != null) + PollAction.Invoke(); + else + Client.SendText(PollString); + } + else + { + Debug.Console(2, this, "Comm not connected"); + } + } + + /// + /// When the client connects, and we're waiting for it, respond and disconect from event + /// + void OneTimeConnectHandler(object o, EventArgs a) + { + if (Client.IsConnected) + { + //Client.IsConnected -= OneTimeConnectHandler; + Debug.Console(2, this, "Comm connected"); + Poll(); + } + } + } + + + public class CommunicationMonitorConfig + { + public int PollInterval { get; set; } + public int TimeToWarning { get; set; } + public int TimeToError { get; set; } + public string PollString { get; set; } + + public CommunicationMonitorConfig() + { + PollInterval = 30000; + TimeToWarning = 120000; + TimeToError = 300000; + PollString = ""; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/Interfaces and things.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/Interfaces and things.cs new file mode 100644 index 00000000..6e4a847e --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/Interfaces and things.cs @@ -0,0 +1,57 @@ + +using System; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + public interface IStatusMonitor + { + IKeyed Parent { get; } + event EventHandler StatusChange; + MonitorStatus Status { get; } + string Message { get; } + BoolFeedback IsOnlineFeedback { get; set; } + void Start(); + void Stop(); + } + + + /// + /// Represents a class that has a basic communication monitoring + /// + public interface ICommunicationMonitor + { + StatusMonitorBase CommunicationMonitor { get; } + } + + /// + /// StatusUnknown = 0, IsOk = 1, InWarning = 2, InError = 3 + /// + public enum MonitorStatus + { + StatusUnknown = 0, + IsOk = 1, + InWarning = 2, + InError = 3 + } + + public class MonitorStatusChangeEventArgs : EventArgs + { + public MonitorStatus Status { get; private set; } + public string Message { get; private set; } + + public MonitorStatusChangeEventArgs(MonitorStatus status) + { + Status = status; + Message = status == MonitorStatus.IsOk ? "" : status.ToString(); + } + + public MonitorStatusChangeEventArgs(MonitorStatus status, string message) + { + Status = status; + Message = message; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorBase.cs new file mode 100644 index 00000000..4abb930f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorBase.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using System.ComponentModel; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + public abstract class StatusMonitorBase : IStatusMonitor, IKeyName + { + public event EventHandler StatusChange; + + /// + /// Format returned: "parentdevkey-comMonitor" + /// + public string Key { get { return Parent.Key + "-comMonitor"; } } + + public string Name { get { return "Comm. monitor"; } } + + public IKeyed Parent { get; private set; } + + public BoolFeedback IsOnlineFeedback { get; set; } + + public bool IsOnline; + + public MonitorStatus Status + { + get { return _Status; } + protected set + { + if (value != _Status) + { + _Status = value; + + OnStatusChange(value); + } + } + } + MonitorStatus _Status; + + public string Message + { + get { return _Message; } + set + { + if (value == _Message) return; + _Message = value; + OnStatusChange(Status, value); + + } + } + string _Message; + + long WarningTime; + long ErrorTime; + CTimer WarningTimer; + CTimer ErrorTimer; + + public StatusMonitorBase(IKeyed parent, long warningTime, long errorTime) + { + Parent = parent; + if (warningTime > errorTime) + throw new ArgumentException("warningTime must be less than errorTime"); + if (warningTime < 5000 || errorTime < 5000) + throw new ArgumentException("time values cannot be less that 5000 ms"); + + IsOnlineFeedback = new BoolFeedback(() => { return IsOnline; }); + Status = MonitorStatus.StatusUnknown; + WarningTime = warningTime; + ErrorTime = errorTime; + } + + public abstract void Start(); + public abstract void Stop(); + + protected void OnStatusChange(MonitorStatus status) + { + if (_Status == MonitorStatus.IsOk) + IsOnline = true; + else + IsOnline = false; + IsOnlineFeedback.FireUpdate(); + var handler = StatusChange; + if (handler != null) + handler(this, new MonitorStatusChangeEventArgs(status)); + } + + protected void OnStatusChange(MonitorStatus status, string message) + { + if (_Status == MonitorStatus.IsOk) + IsOnline = true; + else + IsOnline = false; + IsOnlineFeedback.FireUpdate(); + var handler = StatusChange; + if (handler != null) + handler(this, new MonitorStatusChangeEventArgs(status, message)); + } + + protected void StartErrorTimers() + { + if (WarningTimer == null) WarningTimer = new CTimer(o => { Status = MonitorStatus.InWarning; }, WarningTime); + if (ErrorTimer == null) ErrorTimer = new CTimer(o => { Status = MonitorStatus.InError; }, ErrorTime); + } + + protected void StopErrorTimers() + { + if (WarningTimer != null) WarningTimer.Stop(); + if (ErrorTimer != null) ErrorTimer.Stop(); + WarningTimer = null; + ErrorTimer = null; + } + + protected void ResetErrorTimers() + { + if(WarningTimer != null) + WarningTimer.Reset(WarningTime, WarningTime); + if(ErrorTimer != null) + ErrorTimer.Reset(ErrorTime, ErrorTime); + + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs new file mode 100644 index 00000000..d9b5b33a --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using System.ComponentModel; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + /// + /// + /// + public class StatusMonitorCollection : IStatusMonitor + { + public IKeyed Parent { get; private set; } + + List Monitors = new List(); + + #region IStatusMonitor Members + + public event EventHandler StatusChange; + + public MonitorStatus Status { get; protected set; } + + public string Message { get; private set; } + + public BoolFeedback IsOnlineFeedback { get; set; } + + public StatusMonitorCollection(IKeyed parent) + { + Parent = parent; + } + + public void Start() + { + foreach (var mon in Monitors) + mon.StatusChange += mon_StatusChange; + ProcessStatuses(); + } + + + void ProcessStatuses() + { + var InError = Monitors.Where(m => m.Status == MonitorStatus.InError); + var InWarning = Monitors.Where(m => m.Status == MonitorStatus.InWarning); + var IsOk = Monitors.Where(m => m.Status == MonitorStatus.IsOk); + + + MonitorStatus initialStatus; + string prefix = "0:"; + if (InError.Count() > 0) + { + initialStatus = MonitorStatus.InError; + prefix = "3:"; + } + else if (InWarning.Count() > 0) + { + initialStatus = MonitorStatus.InWarning; + prefix = "2:"; + } + else if (InWarning.Count() > 0) + initialStatus = MonitorStatus.IsOk; + else + initialStatus = MonitorStatus.StatusUnknown; + + // Build the error message string + if (InError.Count() > 0 || InWarning.Count() > 0) + { + StringBuilder sb = new StringBuilder(prefix); + if (InError.Count() > 0) + { + // Do string splits and joins + sb.Append(string.Format("{0} Errors:", InError.Count())); + foreach (var mon in InError) + sb.Append(string.Format("{0}, ", mon.Parent.Key)); + } + if (InWarning.Count() > 0) + { + sb.Append(string.Format("{0} Warnings:", InWarning.Count())); + foreach (var mon in InWarning) + sb.Append(string.Format("{0}, ", mon.Parent.Key)); + } + Message = sb.ToString(); + } + + // Want to fire even if status doesn't change because the message may. + Status = initialStatus; + OnStatusChange(initialStatus, Message); + } + + + void mon_StatusChange(object sender, MonitorStatusChangeEventArgs e) + { + ProcessStatuses(); + } + + public void Stop() + { + throw new NotImplementedException(); + } + + #endregion + + public void AddMonitor(IStatusMonitor monitor) + { + if (!Monitors.Contains(monitor)) + Monitors.Add(monitor); + } + + + protected void OnStatusChange(MonitorStatus status, string message) + { + var handler = StatusChange; + if (handler != null) + handler(this, new MonitorStatusChangeEventArgs(status, message)); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs new file mode 100644 index 00000000..4cc245e0 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs @@ -0,0 +1,404 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.Diagnostics; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace PepperDash.Essentials.Core.Monitoring +{ + /// + /// Wrapper for the static SystemMonitor class to extend functionality and provide external access + /// to SystemMonitor via APIs + /// + public class SystemMonitorController : Device + { + public event EventHandler SystemMonitorPropertiesChanged; + + public Dictionary ProgramStatusFeedbackCollection; + + public IntFeedback TimeZoneFeedback { get; set; } + public StringFeedback TimeZoneTextFeedback { get; set; } + + public StringFeedback IOControllerVersionFeedback { get; set; } + public StringFeedback SnmpVersionFeedback { get; set; } + public StringFeedback BACnetAppVersionFeedback { get; set; } + public StringFeedback ControllerVersionFeedback { get; set; } + + public SystemMonitorController(string key) + : base(key) + { + Debug.Console(2, this, "Adding SystemMonitorController."); + + SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true; + + //CrestronConsole.AddNewConsoleCommand(RefreshSystemMonitorData, "RefreshSystemMonitor", "Refreshes System Monitor Feedbacks", ConsoleAccessLevelEnum.AccessOperator); + + TimeZoneFeedback = new IntFeedback(new Func( () => SystemMonitor.TimeZoneInformation.TimeZoneNumber)); + TimeZoneTextFeedback = new StringFeedback(new Func( () => SystemMonitor.TimeZoneInformation.TimeZoneName)); + + IOControllerVersionFeedback = new StringFeedback(new Func( () => SystemMonitor.VersionInformation.IOPVersion)); + SnmpVersionFeedback = new StringFeedback(new Func( () => SystemMonitor.VersionInformation.SNMPVersion)); + BACnetAppVersionFeedback = new StringFeedback(new Func( () => SystemMonitor.VersionInformation.BACNetVersion)); + ControllerVersionFeedback = new StringFeedback(new Func( () => SystemMonitor.VersionInformation.ControlSystemVersion)); + + //var status = string.Format("System Monitor Status: \r TimeZone: {0}\rTimeZoneText: {1}\rIOControllerVersion: {2}\rSnmpAppVersionFeedback: {3}\rBACnetAppVersionFeedback: {4}\rControllerVersionFeedback: {5}", + // SystemMonitor.TimeZoneInformation.TimeZoneNumber, SystemMonitor.TimeZoneInformation.TimeZoneName, SystemMonitor.VersionInformation.IOPVersion, SystemMonitor.VersionInformation.SNMPVersion, + // SystemMonitor.VersionInformation.BACNetVersion, SystemMonitor.VersionInformation.ControlSystemVersion); + + //Debug.Console(1, this, status); + + ProgramStatusFeedbackCollection = new Dictionary(); + + foreach (var prog in SystemMonitor.ProgramCollection) + { + var program = new ProgramStatusFeedbacks(prog); + ProgramStatusFeedbackCollection.Add(prog.Number, program); + } + + SystemMonitor.ProgramChange += new ProgramStateChangeEventHandler(SystemMonitor_ProgramChange); + SystemMonitor.TimeZoneInformation.TimeZoneChange += new TimeZoneChangeEventHandler(TimeZoneInformation_TimeZoneChange); + } + + /// + /// Gets data in separate thread + /// + /// + void RefreshSystemMonitorData(string command) + { + // this takes a while, launch a new thread + CrestronInvoke.BeginInvoke((o) => + { + TimeZoneFeedback.FireUpdate(); + TimeZoneTextFeedback.FireUpdate(); + IOControllerVersionFeedback.FireUpdate(); + SnmpVersionFeedback.FireUpdate(); + BACnetAppVersionFeedback.FireUpdate(); + ControllerVersionFeedback.FireUpdate(); + + OnSystemMonitorPropertiesChanged(); + } + ); + } + + void OnSystemMonitorPropertiesChanged() + { + var handler = SystemMonitorPropertiesChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + + public override bool CustomActivate() + { + RefreshSystemMonitorData(null); + + return base.CustomActivate(); + } + + //// Sets the time zone + //public void SetTimeZone(int timeZone) + //{ + // SystemMonitor.TimeZoneInformation.TimeZoneNumber = timeZone; + //} + + /// + /// Responds to program change events and triggers the appropriate feedbacks to update + /// + /// + /// + void SystemMonitor_ProgramChange(Program sender, ProgramEventArgs args) + { + Debug.Console(2, this, "Program Change Detected for slot: {0}", sender.Number); + Debug.Console(2, this, "Event Type: {0}", args.EventType); + + var program = ProgramStatusFeedbackCollection[sender.Number]; + + if (args.EventType == eProgramChangeEventType.OperatingState) + { + program.ProgramStartedFeedback.FireUpdate(); + program.ProgramStoppedFeedback.FireUpdate(); + + program.ProgramInfo.OperatingState = args.OperatingState; + + //program.GetProgramInfo(); + + if (args.OperatingState == eProgramOperatingState.Start) + program.GetProgramInfo(); + else + { + program.AggregatedProgramInfoFeedback.FireUpdate(); + program.OnProgramInfoChanged(); + } + } + else if (args.EventType == eProgramChangeEventType.RegistrationState) + { + program.ProgramRegisteredFeedback.FireUpdate(); + program.ProgramUnregisteredFeedback.FireUpdate(); + + program.ProgramInfo.RegistrationState = args.RegistrationState; + + program.GetProgramInfo(); + } + } + + /// + /// Responds to time zone changes and updates the appropriate feedbacks + /// + /// + void TimeZoneInformation_TimeZoneChange(TimeZoneEventArgs args) + { + Debug.Console(2, this, "Time Zone Change Detected."); + TimeZoneFeedback.FireUpdate(); + TimeZoneTextFeedback.FireUpdate(); + + OnSystemMonitorPropertiesChanged(); + } + + + public class ProgramStatusFeedbacks + { + public event EventHandler ProgramInfoChanged; + + public Program Program; + + public ProgramInfo ProgramInfo { get; set; } + + public BoolFeedback ProgramStartedFeedback; + public BoolFeedback ProgramStoppedFeedback; + public BoolFeedback ProgramRegisteredFeedback; + public BoolFeedback ProgramUnregisteredFeedback; + + public StringFeedback ProgramNameFeedback; + public StringFeedback ProgramCompileTimeFeedback; + public StringFeedback CrestronDataBaseVersionFeedback; + // SIMPL windows version + public StringFeedback EnvironmentVersionFeedback; + public StringFeedback AggregatedProgramInfoFeedback; + + public ProgramStatusFeedbacks(Program program) + { + ProgramInfo = new ProgramInfo(program.Number); + + Program = program; + + ProgramInfo.OperatingState = Program.OperatingState; + ProgramInfo.RegistrationState = Program.RegistrationState; + + ProgramStartedFeedback = new BoolFeedback(new Func( () => Program.OperatingState == eProgramOperatingState.Start)); + ProgramStoppedFeedback = new BoolFeedback(new Func( () => Program.OperatingState == eProgramOperatingState.Stop)); + ProgramRegisteredFeedback = new BoolFeedback(new Func( () => Program.RegistrationState == eProgramRegistrationState.Register)); + ProgramUnregisteredFeedback = new BoolFeedback(new Func( () => Program.RegistrationState == eProgramRegistrationState.Unregister)); + + ProgramNameFeedback = new StringFeedback(new Func(() => ProgramInfo.ProgramFile)); + ProgramCompileTimeFeedback = new StringFeedback(new Func(() => ProgramInfo.CompileTime)); + CrestronDataBaseVersionFeedback = new StringFeedback(new Func(() => ProgramInfo.CrestronDB)); + EnvironmentVersionFeedback = new StringFeedback(new Func(() => ProgramInfo.Environment)); + + AggregatedProgramInfoFeedback = new StringFeedback(new Func(() => JsonConvert.SerializeObject(ProgramInfo))); + + GetProgramInfo(); + } + + /// + /// Retrieves information about a running program + /// + public void GetProgramInfo() + { + CrestronInvoke.BeginInvoke((o) => + { + Debug.Console(2, "Attempting to get program info for slot: {0}", Program.Number); + + string response = null; + + var success = CrestronConsole.SendControlSystemCommand(string.Format("progcomments:{0}", Program.Number), ref response); + + if (success) + { + //Debug.Console(2, "Progcomments Response: \r{0}", response); + + if (!response.ToLower().Contains("bad or incomplete")) + { + // Shared properteis + ProgramInfo.ProgramFile = ParseConsoleData(response, "Program File", ": ", "\n"); + ProgramInfo.CompilerRevision = ParseConsoleData(response, "Compiler Rev", ": ", "\n"); + ProgramInfo.CompileTime = ParseConsoleData(response, "Compiled On", ": ", "\n"); + ProgramInfo.Include4Dat = ParseConsoleData(response, "Include4.dat", ": ", "\n"); + + + + if (ProgramInfo.ProgramFile.Contains(".dll")) + { + // SSP Program + ProgramInfo.FriendlyName = ParseConsoleData(response, "Friendly Name", ": ", "\n"); + ProgramInfo.ApplicationName = ParseConsoleData(response, "Application Name", ": ", "\n"); + ProgramInfo.ProgramTool = ParseConsoleData(response, "Program Tool", ": ", "\n"); + ProgramInfo.MinFirmwareVersion = ParseConsoleData(response, "Min Firmware Version", ": ", "\n"); + ProgramInfo.PlugInVersion = ParseConsoleData(response, "PlugInVersion", ": ", "\n"); + } + else if (ProgramInfo.ProgramFile.Contains(".smw")) + { + // SIMPL Windows Program + ProgramInfo.FriendlyName = ParseConsoleData(response, "Friendly Name", ":", "\n"); + ProgramInfo.SystemName = ParseConsoleData(response, "System Name", ": ", "\n"); + ProgramInfo.CrestronDB = ParseConsoleData(response, "CrestronDB", ": ", "\n"); + ProgramInfo.Environment = ParseConsoleData(response, "Source Env", ": ", "\n"); + ProgramInfo.Programmer = ParseConsoleData(response, "Programmer", ": ", "\n"); + + } + //Debug.Console(2, "ProgramInfo: \r{0}", JsonConvert.SerializeObject(ProgramInfo)); + } + else + { + Debug.Console(2, "Bad or incomplete console command response. Initializing ProgramInfo for slot: {0}", Program.Number); + + // Assume no valid program info. Constructing a new object will wipe all properties + ProgramInfo = new ProgramInfo(Program.Number); + + ProgramInfo.OperatingState = Program.OperatingState; + ProgramInfo.RegistrationState = Program.RegistrationState; + } + } + else + { + Debug.Console(2, "Progcomments Attempt Unsuccessful for slot: {0}", Program.Number); + } + + ProgramNameFeedback.FireUpdate(); + ProgramCompileTimeFeedback.FireUpdate(); + CrestronDataBaseVersionFeedback.FireUpdate(); + EnvironmentVersionFeedback.FireUpdate(); + + AggregatedProgramInfoFeedback.FireUpdate(); + + OnProgramInfoChanged(); + }); + } + + public void OnProgramInfoChanged() + { + //Debug.Console(1, "Firing ProgramInfoChanged for slot: {0}", Program.Number); + var handler = ProgramInfoChanged; + if (handler != null) + { + handler(this, new ProgramInfoEventArgs(ProgramInfo)); + } + } + + private string ParseConsoleData(string data, string line, string startString, string endString) + { + string outputData = ""; + + if (data.Length > 0) + { + try + { + + //Debug.Console(2, "ParseConsoleData Data: {0}, Line {1}, startStirng {2}, endString {3}", data, line, startString, endString); + var linePosition = data.IndexOf(line); + var startPosition = data.IndexOf(startString, linePosition) + startString.Length; + var endPosition = data.IndexOf(endString, startPosition); + outputData = data.Substring(startPosition, endPosition - startPosition).Trim(); + //Debug.Console(2, "ParseConsoleData Return: {0}", outputData); + } + catch (Exception e) + { + Debug.Console(1, "Error Parsing Console Data:\r{0}", e); + } + } + + return outputData; + } + } + + } + + /// + /// Class for serializing program slot information + /// + public class ProgramInfo + { + // Shared properties + + [JsonProperty("programNumber")] + public uint ProgramNumber { get; private set; } + + [JsonConverter(typeof(StringEnumConverter))] + [JsonProperty("operatingState")] + public eProgramOperatingState OperatingState { get; set; } + + [JsonConverter(typeof(StringEnumConverter))] + [JsonProperty("registrationState")] + public eProgramRegistrationState RegistrationState { get; set; } + + [JsonProperty("programFile")] + public string ProgramFile { get; set; } + [JsonProperty("friendlyName")] + public string FriendlyName { get; set; } + [JsonProperty("compilerRevision")] + public string CompilerRevision { get; set; } + [JsonProperty("compileTime")] + public string CompileTime { get; set; } + [JsonProperty("include4Dat")] + public string Include4Dat { get; set; } + + // SIMPL Windows properties + [JsonProperty("systemName")] + public string SystemName { get; set; } + [JsonProperty("crestronDb")] + public string CrestronDB { get; set; } + [JsonProperty("environment")] + public string Environment { get; set; } + [JsonProperty("programmer")] + public string Programmer { get; set; } + + + // SSP Properties + [JsonProperty("applicationName")] + public string ApplicationName { get; set; } + [JsonProperty("programTool")] + public string ProgramTool { get; set; } + [JsonProperty("minFirmwareVersion")] + public string MinFirmwareVersion { get; set; } + [JsonProperty("plugInVersion")] + public string PlugInVersion { get; set; } + + public ProgramInfo(uint number) + { + ProgramNumber = number; + + ProgramFile = ""; + FriendlyName = ""; + CompilerRevision = ""; + CompileTime = ""; + Include4Dat = ""; + + SystemName = ""; + CrestronDB = ""; + Environment = ""; + Programmer = ""; + + ApplicationName = ""; + ProgramTool = ""; + MinFirmwareVersion = ""; + PlugInVersion = ""; + } + } + + public class ProgramInfoEventArgs : EventArgs + { + public ProgramInfo ProgramInfo; + + public ProgramInfoEventArgs(ProgramInfo progInfo) + { + ProgramInfo = progInfo; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj new file mode 100644 index 00000000..39cb2235 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -0,0 +1,265 @@ + + + Release + AnyCPU + 9.0.30729 + 2.0 + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5} + Library + Properties + PepperDash.Essentials.Core + PepperDash_Essentials_Core + {0B4745B0-194B-4BB6-8E21-E9057CA92300};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + WindowsCE + E2BECB1F-8C8C-41ba-B736-9BE7D946A398 + 5.0 + SmartDeviceProject1 + v3.5 + Windows CE + + + + + .allowedReferenceRelatedFileExtensions + true + full + false + bin\ + DEBUG;TRACE; + prompt + 4 + 512 + true + true + off + + + .allowedReferenceRelatedFileExtensions + none + true + bin\ + prompt + 4 + 512 + true + true + off + + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.EthernetCommunications.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Fusion.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Remotes.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll + + + + False + ..\..\references\PepperDash_Core.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + False + + + False + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + False + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + False + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpTimerEventInterface.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rem S# Pro preparation will execute after these operations + + \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs new file mode 100644 index 00000000..4f79626f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; + +//using SSMono.IO; + +namespace PepperDash.Essentials.Core.Presets +{ + /// + /// Class that represents the model behind presets display + /// + public class DevicePresetsModel : Device + { + public event EventHandler PresetsLoaded; + + public int PulseTime { get; set; } + public int DigitSpacingMS { get; set; } + public bool PresetsAreLoaded { get; private set; } + + public List PresetsList { get { return _PresetsList.ToList(); } } + List _PresetsList; + public int Count { get { return PresetsList != null ? PresetsList.Count : 0; } } + + public bool UseLocalImageStorage { get; set; } + public string ImagesLocalHostPrefix { get; set; } + public string ImagesPathPrefix { get; set; } + public string ListPathPrefix { get; set; } + + /// + /// The methods on the STB device to call when dialing + /// + Dictionary> DialFunctions; + Action EnterFunction; + + bool DialIsRunning; + string FilePath; + bool InitSuccess; + //SSMono.IO.FileSystemWatcher ListWatcher; + + public DevicePresetsModel(string key, ISetTopBoxNumericKeypad setTopBox, string fileName) + : base(key) + { + PulseTime = 150; + DigitSpacingMS = 150; + + try + { + // Grab the digit functions from the device + // If any fail, the whole thing fails peacefully + DialFunctions = new Dictionary>(10) + { + { '1', setTopBox.Digit1 }, + { '2', setTopBox.Digit2 }, + { '3', setTopBox.Digit3 }, + { '4', setTopBox.Digit4 }, + { '5', setTopBox.Digit5 }, + { '6', setTopBox.Digit6 }, + { '7', setTopBox.Digit7 }, + { '8', setTopBox.Digit8 }, + { '9', setTopBox.Digit9 }, + { '0', setTopBox.Digit0 }, + { '-', setTopBox.Dash } + }; + } + catch + { + Debug.Console(0, "DevicePresets '{0}', not attached to INumericKeypad device. Ignoring", key); + DialFunctions = null; + return; + } + + EnterFunction = setTopBox.KeypadEnter; + + UseLocalImageStorage = true; + + ImagesLocalHostPrefix = "http://" + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS,0); + ImagesPathPrefix = @"/presets/images.zip/"; + ListPathPrefix = @"/html/presets/lists/"; + + SetFileName(fileName); + + //ListWatcher = new FileSystemWatcher(@"\HTML\presets\lists"); + //ListWatcher.NotifyFilter = NotifyFilters.LastWrite; + //ListWatcher.EnableRaisingEvents = true; + //ListWatcher.Changed += ListWatcher_Changed; + InitSuccess = true; + } + + + public void SetFileName(string path) + { + FilePath = ListPathPrefix + path; + LoadChannels(); + } + + public void LoadChannels() + { + PresetsAreLoaded = false; + try + { + var pl = JsonConvert.DeserializeObject(Crestron.SimplSharp.CrestronIO.File.ReadToEnd(FilePath, Encoding.ASCII)); + Name = pl.Name; + _PresetsList = pl.Channels; + } + catch (Exception e) + { + Debug.Console(2, this, "LoadChannels: Error reading presets file. These presets will be empty:\r '{0}'\r Error:{1}", FilePath, e.Message); + // Just save a default empty list + _PresetsList = new List(); + } + PresetsAreLoaded = true; + + var handler = PresetsLoaded; + if (handler != null) + handler(this, EventArgs.Empty); + } + + public void Dial(int presetNum) + { + if (presetNum <= _PresetsList.Count) + Dial(_PresetsList[presetNum - 1].Channel); + } + + public void Dial(string chanNum) + { + if (DialIsRunning || !InitSuccess) return; + if (DialFunctions == null) + { + Debug.Console(1, "DevicePresets '{0}', not attached to keypad device. Ignoring channel", Key); + return; + } + + DialIsRunning = true; + CrestronInvoke.BeginInvoke(o => + { + foreach (var c in chanNum.ToCharArray()) + { + if (DialFunctions.ContainsKey(c)) + Pulse(DialFunctions[c]); + CrestronEnvironment.Sleep(DigitSpacingMS); + } + + if (EnterFunction != null) + Pulse(EnterFunction); + DialIsRunning = false; + }); + } + + void Pulse(Action act) + { + act(true); + CrestronEnvironment.Sleep(PulseTime); + act(false); + } + + /// + /// Event handler for filesystem watcher. When directory changes, this is called + /// + //void ListWatcher_Changed(object sender, FileSystemEventArgs e) + //{ + // Debug.Console(1, this, "folder modified: {0}", e.FullPath); + // if (e.FullPath.Equals(FilePath, StringComparison.OrdinalIgnoreCase)) + // { + // Debug.Console(1, this, "File changed: {0}", e.ChangeType); + // LoadChannels(); + // } + //} + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresetsView.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresetsView.cs new file mode 100644 index 00000000..a43a7a2c --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresetsView.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +namespace PepperDash.Essentials.Core.Presets +{ + public class DevicePresetsView + { + public bool ShowNumbers { get; set; } + public bool ShowName { get; set; } + public bool ShowIcon { get; set; } + + public SubpageReferenceList SRL { get; private set; } + public DevicePresetsModel Model { get; private set; } + + public DevicePresetsView(BasicTriListWithSmartObject tl, DevicePresetsModel model) + { + if (model == null) + { + throw new ArgumentNullException("model", "DevicePresetsView Cannot be instantiated with null model"); + } + ShowIcon = true; + ShowName = true; + + Model = model; + + SRL = new SubpageReferenceList(tl, 10012, 3, 0, 4); + Model.PresetsLoaded += new EventHandler(Model_PresetsLoaded); + } + + public void Attach() + { + if (Model.PresetsAreLoaded) + { + uint index = 1; + foreach (var p in Model.PresetsList) + { + SRL.AddItem(new PresetsListSubpageReferenceListItem(p, index, SRL, this)); + index++; + } + SRL.Count = (ushort)Model.PresetsList.Count; + } + } + + public void Detach() + { + SRL.Clear(); + } + + void Model_PresetsLoaded(object sender, EventArgs e) + { + Detach(); + Attach(); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/Interfaces.cs new file mode 100644 index 00000000..95773c58 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/Interfaces.cs @@ -0,0 +1,22 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; + +//namespace PepperDash.Essentials.Core +//{ +// public interface IPresetsFileChanged : IKeyed +// { +// public event EventHandler PresetsFileChanged; +// } + +// public class PresetsFileChangeEventArgs : EventArgs +// { +// public string FilePath { get; private set; } +// public PresetsFileChangeEventArgs(string filePath) +// { +// FilePath = filePath; +// } +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs new file mode 100644 index 00000000..b9650e60 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core.Presets +{ + public class PresetChannel + { + + [JsonProperty(Required = Required.Always)] + public string Name { get; set; } + [JsonProperty(Required = Required.Always)] + public string IconUrl { get; set; } + [JsonProperty(Required = Required.Always)] + public string Channel { get; set; } + } + + public class PresetsList + { + [JsonProperty(Required=Required.Always)] + public string Name { get; set; } + [JsonProperty(Required = Required.Always)] + public List Channels { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetsListSubpageReferenceListItem.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetsListSubpageReferenceListItem.cs new file mode 100644 index 00000000..78a9d1bf --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetsListSubpageReferenceListItem.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core.Presets +{ + public class PresetsListSubpageReferenceListItem : SubpageReferenceListItem + { + DevicePresetsView View; + PresetChannel Channel; + + public PresetsListSubpageReferenceListItem(PresetChannel chan, uint index, + SubpageReferenceList owner, DevicePresetsView view) + : base(index, owner) + { + View = view; + Channel = chan; + owner.GetBoolFeedbackSig(index, 1).UserObject = new Action(b => { if (!b) view.Model.Dial((int)index); }); + Refresh(); + } + + public override void Clear() + { + Owner.GetBoolFeedbackSig(Index, 1).UserObject = null; + Owner.StringInputSig(Index, 1).StringValue = ""; + Owner.StringInputSig(Index, 2).StringValue = ""; + Owner.StringInputSig(Index, 3).StringValue = ""; + } + + public override void Refresh() + { + var name = View.ShowName ? Channel.Name : ""; + Owner.StringInputSig(Index, 1).StringValue = name; + var chan = View.ShowNumbers ? Channel.Channel : ""; + Owner.StringInputSig(Index, 2).StringValue = chan; + var url = View.Model.ImagesLocalHostPrefix + View.Model.ImagesPathPrefix + Channel.IconUrl; + Debug.Console(2, "icon url={0}", url); + var icon = View.ShowIcon ? url : ""; + Owner.StringInputSig(Index, 3).StringValue = icon; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Properties/AssemblyInfo.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..187d57a6 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyTitle("PepperDashEssentialsBase")] +[assembly: AssemblyCompany("PepperDash Technology Corp")] +[assembly: AssemblyProduct("PepperDashEssentialsBase")] +[assembly: AssemblyCopyright("Copyright © Ppperdash 2019")] +[assembly: AssemblyVersion("1.4.2.*")] \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Properties/ControlSystem.cfg b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Properties/ControlSystem.cfg new file mode 100644 index 00000000..e69de29b diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/REMOVE SigId.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/REMOVE SigId.cs new file mode 100644 index 00000000..884f1601 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/REMOVE SigId.cs @@ -0,0 +1,37 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharpPro; + +//namespace PepperDash.Essentials.Core +//{ +// public class SigId +// { +// public uint Number { get; private set; } +// public eSigType Type { get; private set; } + +// public SigId(eSigType type, uint number) +// { +// Type = type; +// Number = number; +// } + +// public override bool Equals(object id) +// { +// if (id is SigId) +// { +// var sigId = id as SigId; +// return this.Number == sigId.Number && this.Type == sigId.Type; +// } +// else +// return base.Equals(id); +// } + +// public override int GetHashCode() +// { +// return base.GetHashCode(); +// } +// } + +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ramps and Increments/ActionIncrementer.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ramps and Increments/ActionIncrementer.cs new file mode 100644 index 00000000..f63d57bf --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ramps and Increments/ActionIncrementer.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// An incrementer that can use the values of some other object/primitive value to do its thing. + /// It uses an Action to set the value and a Func to get the value from whatever this is + /// attached to. + /// + public class ActionIncrementer + { + public int ChangeAmount { get; set; } + public int MaxValue { get; set; } + public int MinValue { get; set; } + public uint RepeatDelay { get; set; } + public uint RepeatTime { get; set; } + + Action SetAction; + Func GetFunc; + CTimer Timer; + + /// + /// + /// + /// + /// + /// + /// + /// + /// Action that will be called when this needs to set the destination value + /// Func that is called to get the current value + public ActionIncrementer(int changeAmount, int minValue, int maxValue, uint repeatDelay, uint repeatTime, Action setAction, Func getFunc) + { + SetAction = setAction; + GetFunc = getFunc; + ChangeAmount = changeAmount; + MaxValue = maxValue; + MinValue = minValue; + RepeatDelay = repeatDelay; + RepeatTime = repeatTime; + } + + /// + /// Starts incrementing cycle + /// + public void StartUp() + { + if (Timer != null) return; + Go(ChangeAmount); + } + + /// + /// Starts decrementing cycle + /// + public void StartDown() + { + if (Timer != null) return; + Go(-ChangeAmount); + } + + /// + /// Stops the repeat + /// + public void Stop() + { + if (Timer != null) + Timer.Stop(); + Timer = null; + } + + /// + /// Helper that does the work of setting new level, and starting repeat loop, checking against bounds first. + /// + /// + void Go(int change) + { + int currentLevel = GetFunc(); + // Fire once then pause + int newLevel = currentLevel + change; + bool atLimit = CheckLevel(newLevel, out newLevel); + SetAction(newLevel); + + if (atLimit) // Don't go past end + Stop(); + else if (Timer == null) // Only enter the timer if it's not already running + Timer = new CTimer(o => { Go(change); }, null, RepeatDelay, RepeatTime); + } + + /// + /// Helper to check a new level against min/max. Returns revised level if new level + /// will go out of bounds + /// + /// The level to check against bounds + /// Revised level if bounds are exceeded. Min or max + /// true if new level is at or past bounds + bool CheckLevel(int levelIn, out int levelOut) + { + bool isAtLimit = false; + if (levelIn > MaxValue) + { + levelOut = MaxValue; + isAtLimit = true; + } + else if (levelIn < MinValue) + { + levelOut = MinValue; + isAtLimit = true; + } + else + levelOut = levelIn; + return isAtLimit; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ramps and Increments/NumericalHelpers.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ramps and Increments/NumericalHelpers.cs new file mode 100644 index 00000000..50c12ddc --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ramps and Increments/NumericalHelpers.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core +{ + public class NumericalHelpers + { + /// + /// Scales a value + /// + /// + /// + /// + /// + /// + /// + public static double Scale(double input, double inMin, double inMax, double outMin, double outMax) + { + //Debug.Console(2, this, "Scaling (double) input '{0}' with min '{1}'/max '{2}' to output range min '{3}'/max '{4}'", input, inMin, inMax, outMin, outMax); + + double inputRange = inMax - inMin; + + if (inputRange <= 0) + { + throw new ArithmeticException(string.Format("Invalid Input Range '{0}' for Scaling. Min '{1}' Max '{2}'.", inputRange, inMin, inMax)); + } + + double outputRange = outMax - outMin; + + var output = (((input - inMin) * outputRange) / inputRange) + outMin; + + // Debug.Console(2, this, "Scaled output '{0}'", output); + + return output; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ramps and Increments/UshortSigIncrementer.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ramps and Increments/UshortSigIncrementer.cs new file mode 100644 index 00000000..1a6f3c06 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Ramps and Increments/UshortSigIncrementer.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// Attaches to UShortInputSig and does incremental ramping of the signal + /// + public class UshortSigIncrementer + { + UShortInputSig TheSig; + public ushort ChangeAmount { get; set; } + public int MaxValue { get; set; } + public int MinValue { get; set; } + public uint RepeatDelay { get; set; } + public uint RepeatTime { get; set; } + bool SignedMode; + CTimer Timer; + + public UshortSigIncrementer(UShortInputSig sig, ushort changeAmount, int minValue, int maxValue, uint repeatDelay, uint repeatTime) + { + TheSig = sig; + ChangeAmount = changeAmount; + MaxValue = maxValue; + MinValue = minValue; + if (MinValue < 0 || MaxValue < 0) SignedMode = true; + RepeatDelay = repeatDelay; + RepeatTime = repeatTime; + if (SignedMode && (MinValue < -32768 || MaxValue > 32767)) + Debug.Console(1, "UshortSigIncrementer has signed values that exceed range of -32768, 32767"); + } + + public void StartUp() + { + if (Timer != null) return; + Go(ChangeAmount); + } + + public void StartDown() + { + if (Timer != null) return; + Go(-ChangeAmount); + } + + void Go(int change) + { + int level; + if (SignedMode) level = TheSig.ShortValue; + else level = TheSig.UShortValue; + + // Fire once then pause + int newLevel = level + change; + bool atLimit = CheckLevel(newLevel, out newLevel); + SetValue((ushort)newLevel); + + + if (atLimit) // Don't go past end + Stop(); + else if (Timer == null) // Only enter the timer if it's not already running + Timer = new CTimer(o => { Go(change); }, null, RepeatDelay, RepeatTime); + } + + bool CheckLevel(int levelIn, out int levelOut) + { + bool IsAtLimit = false; + if (levelIn > MaxValue) + { + levelOut = MaxValue; + IsAtLimit = true; + } + else if (levelIn < MinValue) + { + levelOut = MinValue; + IsAtLimit = true; + } + else + levelOut = levelIn; + return IsAtLimit; + } + + public void Stop() + { + if (Timer != null) + Timer.Stop(); + Timer = null; + } + + void SetValue(ushort value) + { + //CrestronConsole.PrintLine("Increment level:{0} / {1}", value, (short)value); + TheSig.UShortValue = value; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/SSMonoIOLibrary.clz b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/SSMonoIOLibrary.clz new file mode 100644 index 00000000..1166c23e Binary files /dev/null and b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/SSMonoIOLibrary.clz differ diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/SSMonoIOLibrary.config b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/SSMonoIOLibrary.config new file mode 100644 index 00000000..d1d099df --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/SSMonoIOLibrary.config @@ -0,0 +1,16 @@ + + + SSMonoIOLibrary + SSMonoIOLibrary + SSMonoIOLibrary + 1.007.0017 + SIMPL# Plugin + 5 + 5 + + + + 4/6/2016 7:49:24 AM + 1.0.0.14081 + + \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/SimplSharpData.dat b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/SimplSharpData.dat new file mode 100644 index 00000000..816bfe12 Binary files /dev/null and b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/SimplSharpData.dat differ diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/manifest.info b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/manifest.info new file mode 100644 index 00000000..99eb2339 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/manifest.info @@ -0,0 +1,18 @@ +MainAssembly=SSMonoIOLibrary.dll:6c69af117dca3f74ebca99f7a0e3181c +MainAssemblyMinFirmwareVersion=1.007.0017 +ü +DependencySource=SimplSharpCustomAttributesInterface.dll:9c4b4d4c519b655af90016edca2d66b9 +DependencyPath=SSMonoIOLibrary.clz:SimplSharpCustomAttributesInterface.dll +DependencyMainAssembly=SimplSharpCustomAttributesInterface.dll:9c4b4d4c519b655af90016edca2d66b9 +ü +DependencySource=SimplSharpHelperInterface.dll:aed72eb0e19559a3f56708be76445dcd +DependencyPath=SSMonoIOLibrary.clz:SimplSharpHelperInterface.dll +DependencyMainAssembly=SimplSharpHelperInterface.dll:aed72eb0e19559a3f56708be76445dcd +ü +DependencySource=SimplSharpReflectionInterface.dll:e3ff8edbba84ccd7155b9984e67488b2 +DependencyPath=SSMonoIOLibrary.clz:SimplSharpReflectionInterface.dll +DependencyMainAssembly=SimplSharpReflectionInterface.dll:e3ff8edbba84ccd7155b9984e67488b2 +ü +DependencySource=SSharpCrestronExtensionsLibrary.dll:655a49edee523f150d1c03bcb5db87d0 +DependencyPath=SSMonoIOLibrary.clz:SSharpCrestronExtensionsLibrary.dll +DependencyMainAssembly=SSharpCrestronExtensionsLibrary.dll:655a49edee523f150d1c03bcb5db87d0 diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/manifest.ser b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/manifest.ser new file mode 100644 index 00000000..ac2eb253 Binary files /dev/null and b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoIOLibrary.clz/manifest.ser differ diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/SSMonoProTaskLibrary.config b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/SSMonoProTaskLibrary.config new file mode 100644 index 00000000..3b250815 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/SSMonoProTaskLibrary.config @@ -0,0 +1,16 @@ + + + SSMonoProTaskLibrary + SSMonoProTaskLibrary + SSMonoProTaskLibrary + 1.009.0029 + SIMPL# Plugin + 5 + 5 + + + + 4/6/2016 7:55:41 AM + 1.0.0.14269 + + \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/SSMonoProTaskLibrary.cplz b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/SSMonoProTaskLibrary.cplz new file mode 100644 index 00000000..927a567e Binary files /dev/null and b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/SSMonoProTaskLibrary.cplz differ diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/SimplSharpData.dat b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/SimplSharpData.dat new file mode 100644 index 00000000..816bfe12 Binary files /dev/null and b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/SimplSharpData.dat differ diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/manifest.info b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/manifest.info new file mode 100644 index 00000000..821d5130 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/manifest.info @@ -0,0 +1,30 @@ +MainAssembly=SSMonoProTaskLibrary.dll:5d3a301400516bd812bf1566c72ccbf2 +MainAssemblyMinFirmwareVersion=1.009.0029 +ü +DependencySource=SimplSharpReflectionInterface.dll:e3ff8edbba84ccd7155b9984e67488b2 +DependencyPath=SSMonoProTaskLibrary.cplz:SimplSharpReflectionInterface.dll +DependencyMainAssembly=SimplSharpReflectionInterface.dll:e3ff8edbba84ccd7155b9984e67488b2 +ü +DependencySource=SSharpCrestronExtensionsLibrary.dll:776d0247d8d42164c46c7cc1dfadbd03 +DependencyPath=SSMonoProTaskLibrary.cplz:SSharpCrestronExtensionsLibrary.dll +DependencyMainAssembly=SSharpCrestronExtensionsLibrary.dll:776d0247d8d42164c46c7cc1dfadbd03 +ü +DependencySource=SSMonoConcurrentCollectionsLibrary.dll:b0afcd989b081899c9eb29f9e4c8b799 +DependencyPath=SSMonoProTaskLibrary.cplz:SSMonoConcurrentCollectionsLibrary.dll +DependencyMainAssembly=SSMonoConcurrentCollectionsLibrary.dll:b0afcd989b081899c9eb29f9e4c8b799 +ü +DependencySource=SSMonoProConcurrentCollectionsLibrary.dll:8b718ce29f938bbf9cb5b8fc2d89332f +DependencyPath=SSMonoProTaskLibrary.cplz:SSMonoProConcurrentCollectionsLibrary.dll +DependencyMainAssembly=SSMonoProConcurrentCollectionsLibrary.dll:8b718ce29f938bbf9cb5b8fc2d89332f +ü +DependencySource=SSMonoSupportLibrary.dll:59362515f2c1d61583b2e40793987917 +DependencyPath=SSMonoProTaskLibrary.cplz:SSMonoSupportLibrary.dll +DependencyMainAssembly=SSMonoSupportLibrary.dll:59362515f2c1d61583b2e40793987917 +ü +DependencySource=SSMonoThreadingLibrary.dll:ea2ae2e1d9c425236f39de9192591062 +DependencyPath=SSMonoProTaskLibrary.cplz:SSMonoThreadingLibrary.dll +DependencyMainAssembly=SSMonoThreadingLibrary.dll:ea2ae2e1d9c425236f39de9192591062 +ü +DependencySource=SSMonoTupleLibrary.dll:2a3b419fff4199838079879053fcb41d +DependencyPath=SSMonoProTaskLibrary.cplz:SSMonoTupleLibrary.dll +DependencyMainAssembly=SSMonoTupleLibrary.dll:2a3b419fff4199838079879053fcb41d diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/manifest.ser b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/manifest.ser new file mode 100644 index 00000000..d24ab36c Binary files /dev/null and b/essentials-framework/Essentials Core/PepperDashEssentialsBase/References/SSMonoProTaskLibrary.cplz/manifest.ser differ diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/MOVED RoomEventArgs.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/MOVED RoomEventArgs.cs new file mode 100644 index 00000000..bca9bb62 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/MOVED RoomEventArgs.cs @@ -0,0 +1,41 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; + +//namespace PepperDash.Essentials.Core +//{ +// public class EssentialsRoomSourceChangeEventArgs : EventArgs +// { +// public EssentialsRoom Room { get; private set; } +// public IPresentationSource OldSource { get; private set; } +// public IPresentationSource NewSource { get; private set; } + +// public EssentialsRoomSourceChangeEventArgs(EssentialsRoom room, +// IPresentationSource oldSource, IPresentationSource newSource) +// { +// Room = room; +// OldSource = oldSource; +// NewSource = newSource; +// } +// } + + + +// public class EssentialsRoomAudioDeviceChangeEventArgs : EventArgs +// { +// public EssentialsRoom Room { get; private set; } +// public IBasicVolumeControls OldDevice { get; private set; } +// public IBasicVolumeControls NewDevice { get; private set; } + +// public EssentialsRoomAudioDeviceChangeEventArgs(EssentialsRoom room, +// IBasicVolumeControls oldDevice, IBasicVolumeControls newDevice) +// { +// Room = room; +// OldDevice = oldDevice; +// NewDevice = newDevice; +// } +// } + +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Room.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Room.cs new file mode 100644 index 00000000..5e190bc9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Room.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + //*************************************************************************************************** + + public abstract class Room : Device, IHasFeedback + { + public abstract BoolFeedback RoomIsOnFeedback { get; protected set; } + public abstract BoolFeedback IsCoolingDownFeedback { get; protected set; } + public abstract BoolFeedback IsWarmingUpFeedback { get; protected set; } + + // In concrete classes, these should be computed from the relevant devices + public virtual uint CooldownTime { get { return 10000; } } + public virtual uint WarmupTime { get { return 5000; } } + + public string Description { get; set; } + public string HelpMessage { get; set; } + + public Room(string key, string name) + : base(key, name) + { + Description = ""; + HelpMessage = ""; + } + + public virtual void RoomOn() { } + + public virtual void RoomOff() { } + + #region IDeviceWithOutputs Members + + public virtual FeedbackCollection Feedbacks + { + get + { + return new FeedbackCollection + { + RoomIsOnFeedback, + IsCoolingDownFeedback, + IsWarmingUpFeedback + }; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/RoomCues.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/RoomCues.cs new file mode 100644 index 00000000..0d439d09 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/RoomCues.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials +{ + public static class RoomCue + { + // Commands/ + //bools + public static readonly Cue RoomOnToggle = Cue.BoolCue("RoomOnToggle", 2001); + public static readonly Cue RoomOn = Cue.BoolCue("RoomOn", 2002); + public static readonly Cue RoomOff = Cue.BoolCue("RoomOff", 2003); + public static readonly Cue VolumeUp = Cue.BoolCue("VolumeUp", 2011); + public static readonly Cue VolumeDown = Cue.BoolCue("VolumeDown", 2012); + public static readonly Cue VolumeDefault = Cue.BoolCue("VolumeDefault", 2013); + public static readonly Cue MuteToggle = Cue.BoolCue("MuteToggle", 2014); + public static readonly Cue MuteOn = Cue.BoolCue("MuteOn", 2015); + public static readonly Cue MuteOff = Cue.BoolCue("MuteOff", 2016); + + //ushorts + public static readonly Cue SelectSourceByNumber = Cue.UShortCue("SelectSourceByNumber", 2001); + public static readonly Cue VolumeLevel = Cue.UShortCue("VolumeLevel", 2011); + public static readonly Cue VolumeLevelPercent = Cue.UShortCue("VolumeLevelPercent", 2012); + + //strings + public static readonly Cue SelectSourceByKey = Cue.StringCue("SelectSourceByKey", 2001); + + // Outputs + //Bools + public static readonly Cue RoomIsOn = Cue.BoolCue("RoomIsOn", 2002); + public static readonly Cue RoomIsOnStandby = Cue.BoolCue("RoomIsOnStandby", 2003); + public static readonly Cue RoomIsWarmingUp = Cue.BoolCue("RoomIsWarmingUp", 2004); + public static readonly Cue RoomIsCoolingDown = Cue.BoolCue("RoomIsCoolingDown", 2005); + public static readonly Cue RoomIsOccupied = Cue.BoolCue("RoomIsOccupied", 2006); + + //Ushorts + public static readonly Cue SourcesCount = Cue.UShortCue("SourcesCount", 2001); + public static readonly Cue CurrentSourceNumber = Cue.UShortCue("CurrentSourceNumber", 2002); + public static readonly Cue CurrentSourceType = Cue.UShortCue("CurrentSourceType", 2003); + + //Strings + public static readonly Cue CurrentSourceKey = Cue.StringCue("CurrentSourceKey", 2001); + public static readonly Cue CurrentSourceName = Cue.StringCue("CurrentSourceName", 2002); + + public static readonly Cue VolumeLevelText = Cue.StringCue("VolumeLevelText", 2012); + + public static readonly Cue Key = Cue.StringCue("RoomKey", 2021); + public static readonly Cue Name = Cue.StringCue("RoomName", 2022); + public static readonly Cue Description = Cue.StringCue("Description", 2023); + public static readonly Cue HelpMessage = Cue.StringCue("HelpMessage", 2024); + + //Special + public static readonly Cue Source = new Cue("Source", 0, eCueType.Other); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/IRoutingInputsExtensions.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/IRoutingInputsExtensions.cs new file mode 100644 index 00000000..ad4a3458 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/IRoutingInputsExtensions.cs @@ -0,0 +1,300 @@ +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 static class IRoutingInputsExtensions + { + /// + /// Gets any existing route for a destination, clears it, and then + /// + public static void ReleaseAndMakeRoute(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType) + { + var sw = new Stopwatch(); + sw.Start(); + + destination.ReleaseRoute(); + + if (source == null) return; + var newRoute = destination.GetRouteToSource(source, signalType); + if (newRoute == null) return; + RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute); + Debug.Console(2, destination, "Executing new route"); + newRoute.ExecuteRoutes(); + sw.Stop(); + Debug.Console(2, destination, "Route took {0} ms", sw.ElapsedMilliseconds); + } + + /// + /// Will release the existing route on the destination + /// + /// + /// + public static void ReleaseRoute(this IRoutingInputs destination) + { + var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination); + if (current != null) + { + Debug.Console(2, destination, "Releasing current route: {0}", current.Source.Key); + current.ReleaseRoutes(); + } + } + + + /// + /// + /// + /// + /// + /// + /// + public static RouteDescriptor GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType) + { + var routeTable = new RouteDescriptor (source, destination, signalType); + + Debug.Console(0, destination, "Attempting to build source route from {0}***", source.Key); + if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeTable)) + routeTable = null; + + Debug.Console(0, destination, "Route{0} discovered ***", routeTable == null ? " NOT" : ""); + return routeTable; + } + + /// + /// 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// true if source is hit + static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, + RoutingOutputPort onSuccessOutputPort, List alreadyCheckedDevices, + eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable) + { + cycle++; + Debug.Console(0, destination, "SelectInput-cycle {1}. Finding {2} route back to {0}", source.Key, cycle, signalType); + var destDevInputTies = TieLineCollection.Default.Where(t => + t.DestinationPort.ParentDevice == destination && (t.Type == signalType || t.Type == eRoutingSignalType.AudioVideo)); + + // find a direct tie + var directTie = destDevInputTies.FirstOrDefault( + t => !(t.SourcePort.ParentDevice is IRoutingInputsOutputs) + && t.DestinationPort.ParentDevice == destination + && t.SourcePort.ParentDevice == source); + RoutingInputPort inputPort = null; + if (directTie != null) // Found a tie directly to the source + { + Debug.Console(0, destination, "Found direct tie to {0}**", source.Key); + inputPort = directTie.DestinationPort; + } + else // no direct-connect. Walk back devices. + { + Debug.Console(0, 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); + foreach (var inputTieToTry in attachedMidpoints) + { + Debug.Console(0, destination, "Trying to find route on {0}", inputTieToTry.SourcePort.ParentDevice.Key); + var upstreamDeviceOutputPort = inputTieToTry.SourcePort; + var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs; + // Check if this previous device has already been walked + if (!(alreadyCheckedDevices != null && alreadyCheckedDevices.Contains(upstreamRoutingDevice))) + { + // 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.Console(0, destination, "Upstream device route found"); + inputPort = inputTieToTry.DestinationPort; + break; // Stop looping the inputs in this cycle + } + } + } + } + + // we have a route on corresponding inputPort. *** Do the route *** + if (inputPort != null) + { + Debug.Console(0, destination, "adding route:"); + if (onSuccessOutputPort == null) + { + // it's a sink device + routeTable.Routes.Add(new RouteSwitchDescriptor(inputPort)); + } + else if (destination is IRouting) + { + routeTable.Routes.Add(new RouteSwitchDescriptor (onSuccessOutputPort, inputPort)); + } + else // device is merely IRoutingInputOutputs + Debug.Console(0, destination, " No routing. Passthrough device"); + Debug.Console(0, destination, "Exiting cycle {0}", cycle); + return true; + } + + if(alreadyCheckedDevices == null) + alreadyCheckedDevices = new List(); + alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs); + + Debug.Console(0, 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 + + + /// + /// A collection of routes - typically the global DefaultCollection is used + /// + public class RouteDescriptorCollection + { + public static RouteDescriptorCollection DefaultCollection + { + get + { + if (_DefaultCollection == null) + _DefaultCollection = new RouteDescriptorCollection(); + return _DefaultCollection; + } + } + static RouteDescriptorCollection _DefaultCollection; + + List RouteDescriptors = new List(); + + public void AddRouteDescriptor(RouteDescriptor descriptor) + { + if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination)) + { + Debug.Console(1, descriptor.Destination, + "Route to [{0}] already exists in global routes table", descriptor.Source.Key); + return; + } + RouteDescriptors.Add(descriptor); + } + + public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination) + { + return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination); + } + + /// + /// Returns the RouteDescriptor for a given destination and removes it from collection. + /// Returns null if no route with the provided destination exists. + /// + public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination) + { + var descr = GetRouteDescriptorForDestination(destination); + if (descr != null) + RouteDescriptors.Remove(descr); + return descr; + } + } + + /// + /// Represents an collection of individual route steps between Source and Destination + /// + public class RouteDescriptor + { + public IRoutingInputs Destination { get; private set; } + public IRoutingOutputs Source { get; private set; } + public eRoutingSignalType SignalType { get; private set; } + public List Routes { get; private set; } + + + public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType) + { + Destination = destination; + Source = source; + SignalType = signalType; + Routes = new List(); + } + + public void ExecuteRoutes() + { + foreach (var route in Routes) + { + Debug.Console(2, route.ToString()); + if (route.SwitchingDevice is IRoutingSinkWithSwitching) + (route.SwitchingDevice as IRoutingSinkWithSwitching).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"); + } + } + } + + 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"); + } + } + } + + 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)); + } + } + + /// + /// Represents an individual link for a route + /// + 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(OutputPort == null) // IRoutingSink + return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector); + + return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/RoutingInterfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/RoutingInterfaces.cs new file mode 100644 index 00000000..352a35dc --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/RoutingInterfaces.cs @@ -0,0 +1,57 @@ +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 +{ + //******************************************************************************************* + // Interfaces + + public interface IRoutingInputs : IKeyed + { + RoutingPortCollection InputPorts { get; } + } + + public interface IRoutingOutputs : IKeyed + { + RoutingPortCollection OutputPorts { get; } + } + + /// + /// For fixed-source endpoint devices + /// + public interface IRoutingSinkNoSwitching : IRoutingInputs + { + + } + + public interface IRoutingSinkWithSwitching : IRoutingSinkNoSwitching + { + //void ClearRoute(); + void ExecuteSwitch(object inputSelector); + } + + /// + /// For devices like RMCs, baluns, other devices with no switching. + /// + public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs + { + } + + public interface IRouting : IRoutingInputsOutputs + { + //void ClearRoute(object outputSelector); + void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType); + } + + public interface IRoutingSource : IRoutingOutputs + { + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/RoutingPort.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/RoutingPort.cs new file mode 100644 index 00000000..e2a026ab --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/RoutingPort.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + /// + /// Base class for RoutingInput and Output ports + /// + public abstract class RoutingPort : IKeyed + { + public string Key { get; private set; } + public eRoutingSignalType Type { get; private set; } + public eRoutingPortConnectionType ConnectionType { get; private set; } + public readonly object Selector; + public bool IsInternal { get; private set; } + + public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, bool isInternal) + { + Key = key; + Type = type; + ConnectionType = connType; + Selector = selector; + IsInternal = IsInternal; + } + } + + public enum eRoutingSignalType + { + Audio, + Video, + AudioVideo + } + + public enum eRoutingPortConnectionType + { + None, BackplaneOnly, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi, Composite, Component, DmCat, DmMmFiber, DmSmFiber + } + + /// + /// Basic RoutingInput with no statuses. + /// + public class RoutingInputPort : RoutingPort + { + /// + /// The IRoutingInputs object this lives on + /// + public IRoutingInputs ParentDevice { get; private set; } + + /// + /// Constructor for a basic RoutingInputPort + /// + /// An object used to refer to this port in the IRouting device's ExecuteSwitch method. + /// May be string, number, whatever + /// The IRoutingInputs object this lives on + public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, + object selector, IRoutingInputs parent) + : this (key, type, connType, selector, parent, false) + { + } + + /// + /// Constructor for a virtual routing input port that lives inside a device. For example + /// the ports that link a DM card to a DM matrix bus + /// + /// true for internal ports + public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, + object selector, IRoutingInputs parent, bool isInternal) + : base(key, type, connType, selector, isInternal) + { + if (parent == null) + throw new ArgumentNullException("parent"); + ParentDevice = parent; + } + + } + + /// + /// A RoutingInputPort for devices like DM-TX and DM input cards. + /// Will provide video statistics on connected signals + /// + public class RoutingInputPortWithVideoStatuses : RoutingInputPort + { + /// + /// Video statuses attached to this port + /// + public VideoStatusOutputs VideoStatus { get; private set; } + + /// + /// Constructor + /// + /// An object used to refer to this port in the IRouting device's ExecuteSwitch method. + /// May be string, number, whatever + /// The IRoutingInputs object this lives on + /// A VideoStatusFuncsWrapper used to assign the callback funcs that will get + /// the values for the various stats + public RoutingInputPortWithVideoStatuses(string key, + eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, + IRoutingInputs parent, VideoStatusFuncsWrapper funcs) : + base(key, type, connType, selector, parent) + { + VideoStatus = new VideoStatusOutputs(funcs); + } + } + + public class RoutingOutputPort : RoutingPort + { + /// + /// The IRoutingOutputs object this port lives on + /// + public IRoutingOutputs ParentDevice { get; private set; } + + public InUseTracking InUseTracker { get; private set; } + + + /// + /// + /// An object used to refer to this port in the IRouting device's ExecuteSwitch method. + /// May be string, number, whatever + /// The IRoutingOutputs object this port lives on + public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, + object selector, IRoutingOutputs parent) + : this(key, type, connType, selector, parent, false) + { + } + + public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, + object selector, IRoutingOutputs parent, bool isInternal) + : base(key, type, connType, selector, isInternal) + { + if (parent == null) + throw new ArgumentNullException("parent"); + ParentDevice = parent; + InUseTracker = new InUseTracking(); + } + + public override string ToString() + { + return ParentDevice.Key + ":" + Key; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/RoutingPortCollection.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/RoutingPortCollection.cs new file mode 100644 index 00000000..ba972ab7 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/RoutingPortCollection.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + /// + /// Basically a List , with an indexer to find ports by key name + /// + public class RoutingPortCollection : List where T: RoutingPort + { + /// + /// Case-insensitive port lookup linked to ports' keys + /// + public T this[string key] + { + get + { + return this.FirstOrDefault(i => i.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); + } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/TieLine.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/TieLine.cs new file mode 100644 index 00000000..6a89d2de --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing-CHECK REMOVE/TieLine.cs @@ -0,0 +1,142 @@ +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 class TieLine + { + public RoutingOutputPort SourcePort { get; private set; } + public RoutingInputPort DestinationPort { get; private set; } + public int InUseCount { get { return DestinationUsingThis.Count; } } + + /// + /// Gets the type of this tie line. Will either be the type of the desination port + /// or the type of OverrideType when it is set. + /// + public eRoutingSignalType Type + { + get + { + if (OverrideType.HasValue) return OverrideType.Value; + return DestinationPort.Type; + } + } + + /// + /// Use this to override the Type property for the destination port. For example, + /// when the tie line is type AudioVideo, and the signal flow should be limited to + /// Audio-only or Video only, changing this type will alter the signal paths + /// available to the routing algorithm without affecting the actual Type + /// of the destination port. + /// + public eRoutingSignalType? OverrideType { get; set; } + + List DestinationUsingThis = new List(); + + /// + /// For tie lines that represent internal links, like from cards to the matrix in a DM. + /// This property is true if SourcePort and DestinationPort IsInternal + /// property are both true + /// + public bool IsInternal { get { return SourcePort.IsInternal && DestinationPort.IsInternal; } } + public bool TypeMismatch { get { return SourcePort.Type != DestinationPort.Type; } } + public bool ConnectionTypeMismatch { get { return SourcePort.ConnectionType != DestinationPort.ConnectionType; } } + public string TypeMismatchNote { get; set; } + + /// + /// + /// + /// + /// + public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort) + { + if (sourcePort == null || destinationPort == null) + throw new ArgumentNullException("source or destination port"); + SourcePort = sourcePort; + DestinationPort = destinationPort; + } + + /// + /// Creates a tie line with an overriding Type. See help for OverrideType property for info + /// + /// The signal type to limit the link to. Overrides DestinationPort.Type + public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType overrideType) : + this(sourcePort, destinationPort) + { + OverrideType = overrideType; + } + + public static TieLine TieLineFromStrings(string sourceKey, string sourcePortKey, string destinationKey, string destinationPortKey) + { + var sourceDev = DeviceManager.GetDeviceForKey(sourceKey) as IRoutingOutputs; + if (sourceDev == null) + { + Debug.Console(1, "WARNING: Cannot create tie line, routable source '{0}' not found", sourceKey); + return null; + } + var destDev = DeviceManager.GetDeviceForKey(destinationKey) as IRoutingInputs; + if (destDev == null) + { + Debug.Console(1, "WARNING: Cannot create tie line, routable destination '{0}' not found", destinationKey); + return null; + } + var sourcePort = sourceDev.OutputPorts[sourcePortKey]; + if (sourcePort == null) + { + Debug.Console(1, "WARNING: Cannot create tie line. Source '{0}' does not contain port '{1}'", sourceKey, sourcePortKey); + return null; + } + var destPort = destDev.InputPorts[destinationPortKey]; + if (destPort == null) + { + Debug.Console(1, "WARNING: Cannot create tie line. Destination '{0}' does not contain port '{1}'", destinationKey, destinationPortKey); + return null; + } + + return new TieLine(sourcePort, destPort); + } + + /// + /// Will link up video status from supporting inputs to connected outputs + /// + public void Activate() + { + // Now does nothing + } + + public void Deactivate() + { + // Now does nothing + } + + public override string ToString() + { + return string.Format("Tie line: [{0}]{1} --> [{2}]{3}", SourcePort.ParentDevice.Key, SourcePort.Key, + DestinationPort.ParentDevice.Key, DestinationPort.Key); + } + } + + + //******************************************************************************** + + public class TieLineCollection : List + { + public static TieLineCollection Default + { + get + { + if (_Default == null) + _Default = new TieLineCollection(); + return _Default; + } + } + static TieLineCollection _Default; + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/DummyRoutingInputsDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/DummyRoutingInputsDevice.cs new file mode 100644 index 00000000..9a92c2ea --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/DummyRoutingInputsDevice.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.Routing +{ + public class DummyRoutingInputsDevice : Device, IRoutingSource + { + /// + /// A single output port, backplane, audioVideo + /// + public RoutingOutputPort AudioVideoOutputPort { get; private set; } + + /// + /// contains the output port + /// + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection() { AudioVideoOutputPort }; } + } + + /// + /// constructor + /// + /// key for special device + public DummyRoutingInputsDevice(string key) : base(key) + { + AudioVideoOutputPort = new RoutingOutputPort("internal", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.BackplaneOnly, + null, this, true); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/ICardPortsDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/ICardPortsDevice.cs new file mode 100644 index 00000000..6f9ea22f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/ICardPortsDevice.cs @@ -0,0 +1,20 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; + +//using PepperDash.Core; + +//namespace PepperDash.Essentials.Core +//{ +// /// +// /// Defines a class that has cards, like a DM chassis controller, where +// /// we need to access ports on those cards +// /// +// public interface ICardPortsDevice : IKeyed +// { +// RoutingInputPort GetChildInputPort(string card, string port); +// RoutingOutputPort GetChildOutputPort(string card, string port); +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/IRoutingInputsExtensions.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/IRoutingInputsExtensions.cs new file mode 100644 index 00000000..d92a0202 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/IRoutingInputsExtensions.cs @@ -0,0 +1,333 @@ +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 +{ + /// + /// Extensions added to any IRoutingInputs classes to provide discovery-based routing + /// on those destinations. + /// + public static class IRoutingInputsExtensions + { + /// + /// 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 + /// + public static void ReleaseAndMakeRoute(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType) + { + destination.ReleaseRoute(); + + if (source == null) return; + var newRoute = destination.GetRouteToSource(source, signalType); + if (newRoute == null) return; + RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute); + Debug.Console(2, destination, "Executing full route"); + newRoute.ExecuteRoutes(); + } + + /// + /// Will release the existing route on the destination, if it is found in + /// RouteDescriptorCollection.DefaultCollection + /// + /// + public static void ReleaseRoute(this IRoutingInputs destination) + { + var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination); + if (current != null) + { + Debug.Console(1, destination, "Releasing current route: {0}", current.Source.Key); + current.ReleaseRoutes(); + } + } + + /// + /// 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 + /// + public static RouteDescriptor GetRouteToSource(this IRoutingInputs 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.AudioVideo) + { + Debug.Console(1, 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.Console(1, 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.Console(1, destination, "Cannot find audio route to {0}", source.Key); + var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescr); + if (!videoSuccess) + Debug.Console(1, destination, "Cannot find video route to {0}", source.Key); + if (!audioSuccess && !videoSuccess) + routeDescr = null; + } + + //Debug.Console(1, destination, "Route{0} discovered", routeDescr == null ? " NOT" : ""); + return routeDescr; + } + + /// + /// 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. + /// + /// + /// + /// The RoutingOutputPort whose link is being checked for a route + /// Prevents Devices from being twice-checked + /// This recursive function should not be called with AudioVideo + /// Just an informational counter + /// The RouteDescriptor being populated as the route is discovered + /// true if source is hit + static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, + RoutingOutputPort outputPortToUse, List alreadyCheckedDevices, + eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable) + { + cycle++; + Debug.Console(2, "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.AudioVideo)); + + // find a direct tie + var directTie = destDevInputTies.FirstOrDefault( + t => t.DestinationPort.ParentDevice == destination + && t.SourcePort.ParentDevice == source); + if (directTie != null) // Found a tie directly to the source + { + goodInputPort = directTie.DestinationPort; + } + else // no direct-connect. Walk back devices. + { + Debug.Console(2, 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); + foreach (var inputTieToTry in attachedMidpoints) + { + Debug.Console(2, destination, "Trying to find route on {0}", inputTieToTry.SourcePort.ParentDevice.Key); + var upstreamDeviceOutputPort = inputTieToTry.SourcePort; + var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs; + // Check if this previous device has already been walked + if (!(alreadyCheckedDevices != null && alreadyCheckedDevices.Contains(upstreamRoutingDevice))) + { + // 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.Console(2, 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.Console(2, 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.Console(2, destination, " No routing. Passthrough device"); + //Debug.Console(2, destination, "Exiting cycle {0}", cycle); + return true; + } + + if(alreadyCheckedDevices == null) + alreadyCheckedDevices = new List(); + alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs); + + Debug.Console(2, 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 + + + /// + /// A collection of RouteDescriptors - typically the static DefaultCollection is used + /// + public class RouteDescriptorCollection + { + public static RouteDescriptorCollection DefaultCollection + { + get + { + if (_DefaultCollection == null) + _DefaultCollection = new RouteDescriptorCollection(); + return _DefaultCollection; + } + } + static RouteDescriptorCollection _DefaultCollection; + + List RouteDescriptors = new List(); + + /// + /// 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. + /// + /// + public void AddRouteDescriptor(RouteDescriptor descriptor) + { + if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination)) + { + Debug.Console(1, descriptor.Destination, + "Route to [{0}] already exists in global routes table", descriptor.Source.Key); + return; + } + RouteDescriptors.Add(descriptor); + } + + /// + /// Gets the RouteDescriptor for a destination + /// + /// null if no RouteDescriptor for a destination exists + public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination) + { + return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination); + } + + /// + /// Returns the RouteDescriptor for a given destination AND removes it from collection. + /// Returns null if no route with the provided destination exists. + /// + public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination) + { + var descr = GetRouteDescriptorForDestination(destination); + if (descr != null) + RouteDescriptors.Remove(descr); + return descr; + } + } + + /// + /// Represents an collection of individual route steps between Source and Destination + /// + public class RouteDescriptor + { + public IRoutingInputs Destination { get; private set; } + public IRoutingOutputs Source { get; private set; } + public eRoutingSignalType SignalType { get; private set; } + public List Routes { get; private set; } + + + public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType) + { + Destination = destination; + Source = source; + SignalType = signalType; + Routes = new List(); + } + + /// + /// Executes all routes described in this collection. Typically called via + /// extension method IRoutingInputs.ReleaseAndMakeRoute() + /// + public void ExecuteRoutes() + { + foreach (var route in Routes) + { + Debug.Console(2, "ExecuteRoutes: {0}", route.ToString()); + if (route.SwitchingDevice is IRoutingSinkWithSwitching) + (route.SwitchingDevice as IRoutingSinkWithSwitching).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.Console(2, "Output port {0} routing. Count={1}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue); + } + } + } + + /// + /// Releases all routes in this collection. Typically called via + /// extension method IRoutingInputs.ReleaseAndMakeRoute() + /// + 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.Console(2, "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)); + } + } + + /// + /// Represents an individual link for a route + /// + 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); + + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs new file mode 100644 index 00000000..76745f22 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingInterfaces.cs @@ -0,0 +1,84 @@ +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 +{ + //******************************************************************************************* + // Interfaces + + + /// + /// Defines a class that has a collection of RoutingInputPorts + /// + public interface IRoutingInputs : IKeyed + { + RoutingPortCollection InputPorts { get; } + } + + /// + /// Defines a class that has a collection of RoutingOutputPorts + /// + + public interface IRoutingOutputs : IKeyed + { + RoutingPortCollection OutputPorts { get; } + } + + /// + /// For fixed-source endpoint devices + /// + public interface IRoutingSinkNoSwitching : IRoutingInputs + { + + } + + /// + /// Endpoint device like a display, that selects inputs + /// + public interface IRoutingSinkWithSwitching : IRoutingSinkNoSwitching + { + //void ClearRoute(); + void ExecuteSwitch(object inputSelector); + } + + /// + /// For devices like RMCs, baluns, other devices with no switching. + /// + public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs + { + } + + /// + /// 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. + /// + public interface IRouting : IRoutingInputsOutputs + { + //void ClearRoute(object outputSelector); + void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType); + } + + public interface ITxRouting : IRouting + { + IntFeedback VideoSourceNumericFeedback { get; } + IntFeedback AudioSourceNumericFeedback { get; } + void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type); + } + + /// + /// Defines an IRoutingOutputs devices as being a source - the start of the chain + /// + public interface IRoutingSource : IRoutingOutputs + { + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs new file mode 100644 index 00000000..47e47501 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + /// + /// Base class for RoutingInput and Output ports + /// + public abstract class RoutingPort : IKeyed + { + public string Key { get; private set; } + public eRoutingSignalType Type { get; private set; } + public eRoutingPortConnectionType ConnectionType { get; private set; } + public readonly object Selector; + public bool IsInternal { get; private set; } + public object FeedbackMatchObject { get; set; } + public object Port { get; set; } + + public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, bool isInternal) + { + Key = key; + Type = type; + ConnectionType = connType; + Selector = selector; + IsInternal = IsInternal; + } + } + + public enum eRoutingSignalType + { + Audio = 1, + Video = 2, + AudioVideo = 3 + } + + public enum eRoutingPortConnectionType + { + None, BackplaneOnly, DisplayPort, Dvi, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi, + Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming + } + + /// + /// Basic RoutingInput with no statuses. + /// + public class RoutingInputPort : RoutingPort + { + /// + /// The IRoutingInputs object this lives on + /// + public IRoutingInputs ParentDevice { get; private set; } + + /// + /// Constructor for a basic RoutingInputPort + /// + /// An object used to refer to this port in the IRouting device's ExecuteSwitch method. + /// May be string, number, whatever + /// The IRoutingInputs object this lives on + public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, + object selector, IRoutingInputs parent) + : this (key, type, connType, selector, parent, false) + { + } + + /// + /// Constructor for a virtual routing input port that lives inside a device. For example + /// the ports that link a DM card to a DM matrix bus + /// + /// true for internal ports + public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, + object selector, IRoutingInputs parent, bool isInternal) + : base(key, type, connType, selector, isInternal) + { + if (parent == null) + throw new ArgumentNullException("parent"); + ParentDevice = parent; + } + + + + + ///// + ///// Static method to get a named port from a named device + ///// + ///// Returns null if device or port doesn't exist + //public static RoutingInputPort GetDevicePort(string deviceKey, string portKey) + //{ + // var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as IRoutingInputs; + // if (sourceDev == null) + // return null; + // return sourceDev.InputPorts[portKey]; + //} + + ///// + ///// Static method to get a named port from a card in a named ICardPortsDevice device + ///// Uses ICardPortsDevice.GetChildInputPort + ///// + ///// 'input-N' + ///// null if device, card or port doesn't exist + //public static RoutingInputPort GetDeviceCardPort(string deviceKey, string cardKey, string portKey) + //{ + // var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as ICardPortsDevice; + // if (sourceDev == null) + // return null; + // return sourceDev.GetChildInputPort(cardKey, portKey); + //} + } + + /// + /// A RoutingInputPort for devices like DM-TX and DM input cards. + /// Will provide video statistics on connected signals + /// + public class RoutingInputPortWithVideoStatuses : RoutingInputPort + { + /// + /// Video statuses attached to this port + /// + public VideoStatusOutputs VideoStatus { get; private set; } + + /// + /// Constructor + /// + /// An object used to refer to this port in the IRouting device's ExecuteSwitch method. + /// May be string, number, whatever + /// The IRoutingInputs object this lives on + /// A VideoStatusFuncsWrapper used to assign the callback funcs that will get + /// the values for the various stats + public RoutingInputPortWithVideoStatuses(string key, + eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, + IRoutingInputs parent, VideoStatusFuncsWrapper funcs) : + base(key, type, connType, selector, parent) + { + VideoStatus = new VideoStatusOutputs(funcs); + } + } + + public class RoutingOutputPort : RoutingPort + { + /// + /// The IRoutingOutputs object this port lives on + /// + public IRoutingOutputs ParentDevice { get; private set; } + + public InUseTracking InUseTracker { get; private set; } + + + /// + /// + /// An object used to refer to this port in the IRouting device's ExecuteSwitch method. + /// May be string, number, whatever + /// The IRoutingOutputs object this port lives on + public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, + object selector, IRoutingOutputs parent) + : this(key, type, connType, selector, parent, false) + { + } + + public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, + object selector, IRoutingOutputs parent, bool isInternal) + : base(key, type, connType, selector, isInternal) + { + if (parent == null) + throw new ArgumentNullException("parent"); + ParentDevice = parent; + InUseTracker = new InUseTracking(); + } + + public override string ToString() + { + return ParentDevice.Key + ":" + Key; + } + + ///// + ///// Static method to get a named port from a named device + ///// + ///// Returns null if device or port doesn't exist + //public static RoutingOutputPort GetDevicePort(string deviceKey, string portKey) + //{ + // var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as IRoutingOutputs; + // if (sourceDev == null) + // return null; + // var port = sourceDev.OutputPorts[portKey]; + // if (port == null) + // Debug.Console(0, "WARNING: Device '{0}' does does not contain output port '{1}'", deviceKey, portKey); + // return port; + //} + + ///// + ///// Static method to get a named port from a card in a named ICardPortsDevice device + ///// Uses ICardPortsDevice.GetChildOutputPort on that device + ///// + ///// 'input-N' or 'output-N' + ///// null if device, card or port doesn't exist + //public static RoutingOutputPort GetDeviceCardPort(string deviceKey, string cardKey, string portKey) + //{ + // var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as ICardPortsDevice; + // if (sourceDev == null) + // return null; + // var port = sourceDev.GetChildOutputPort(cardKey, portKey); + //} + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPortCollection.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPortCollection.cs new file mode 100644 index 00000000..ba972ab7 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPortCollection.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + /// + /// Basically a List , with an indexer to find ports by key name + /// + public class RoutingPortCollection : List where T: RoutingPort + { + /// + /// Case-insensitive port lookup linked to ports' keys + /// + public T this[string key] + { + get + { + return this.FirstOrDefault(i => i.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); + } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPortNames.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPortNames.cs new file mode 100644 index 00000000..00e85191 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPortNames.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Core.Routing +{ + /// + /// These should correspond directly with the portNames var in the config tool. + /// + public class RoutingPortNames + { + /// + /// antennaIn + /// + public const string AntennaIn = "antennaIn"; + /// + /// anyAudioIn + /// + public const string AnyAudioIn = "anyAudioIn"; + /// + /// anyAudioOut + /// + public const string AnyAudioOut = "anyAudioOut"; + /// + /// anyOut + /// + public const string AnyOut = "anyOut"; + /// + /// anyVideoIn + /// + public const string AnyVideoIn = "anyVideoIn"; + /// + /// anyVideoOut + /// + public const string AnyVideoOut = "anyVideoOut"; + /// + /// balancedAudioOut + /// + public const string BalancedAudioOut = "balancedAudioOut"; + /// + /// codecOsd + /// + public const string CodecOsd = "codecOsd"; + /// + /// componentIn + /// + public const string ComponentIn = "componentIn"; + /// + /// componentOut + /// + public const string ComponentOut = "componentOut"; + /// + /// compositeIn + /// + public const string CompositeIn = "compositeIn"; + /// + /// compositeOut + /// + public const string CompositeOut = "compositeOut"; + /// + /// displayPortIn + /// + public const string DisplayPortIn = "displayPortIn"; + /// + /// displayPortIn1 + /// + public const string DisplayPortIn1 = "displayPortIn1"; + /// + /// displayPortIn2 + /// + public const string DisplayPortIn2 = "displayPortIn2"; + /// + /// displayPortIn3 + /// + public const string DisplayPortIn3 = "displayPortIn3"; + /// + /// displayPortOut + /// + public const string DisplayPortOut = "displayPortOut"; + /// + /// dmIn + /// + public const string DmIn = "dmIn"; + /// + /// dmOut + /// + public const string DmOut = "dmOut"; + /// + /// dviIn + /// + public const string DviIn = "dviIn"; + /// + /// dviIn1 + /// + public const string DviIn1 = "dviIn1"; + /// + /// dviOut + /// + public const string DviOut = "dviOut"; + /// + /// hdmiIn + /// + public const string HdmiIn = "hdmiIn"; + /// + /// hdmiIn1 + /// + public const string HdmiIn1 = "hdmiIn1"; + /// + /// hdmiIn1PC + /// + public const string HdmiIn1PC = "hdmiIn1PC"; + /// + /// hdmiIn2 + /// + public const string HdmiIn2 = "hdmiIn2"; + /// + /// hdmiIn2PC + /// + public const string HdmiIn2PC = "hdmiIn2PC"; + /// + /// hdmiIn3 + /// + public const string HdmiIn3 = "hdmiIn3"; + /// + /// hdmiIn4 + /// + public const string HdmiIn4 = "hdmiIn4"; + /// + /// hdmiIn5 + /// + public const string HdmiIn5 = "hdmiIn5"; + /// + /// hdmiIn6 + /// + public const string HdmiIn6 = "hdmiIn6"; + /// + /// hdmiOut + /// + public const string HdmiOut = "hdmiOut"; + /// + /// hdmiOut1 + /// + public const string HdmiOut1 = "hdmiOut1"; + /// + /// hdmiOut2 + /// + public const string HdmiOut2 = "hdmiOut2"; + /// + /// hdmiOut3 + /// + public const string HdmiOut3 = "hdmiOut3"; + /// + /// hdmiOut4 + /// + public const string HdmiOut4 = "hdmiOut4"; + /// + /// hdmiOut5 + /// + public const string HdmiOut5 = "hdmiOut5"; + /// + /// hdmiOut6 + /// + public const string HdmiOut6 = "hdmiOut6"; + /// + /// none + /// + public const string None = "none"; + /// + /// rgbIn + /// + public const string RgbIn = "rgbIn"; + /// + /// rgbIn1 + /// + public const string RgbIn1 = "rgbIn1"; + /// + /// rgbIn2 + /// + public const string RgbIn2 = "rgbIn2"; + /// + /// vgaIn + /// + public const string VgaIn = "vgaIn"; + /// + /// vgaIn1 + /// + public const string VgaIn1 = "vgaIn1"; + /// + /// vgaOut + /// + public const string VgaOut = "vgaOut"; + /// + /// IPC/OPS + /// + public const string IpcOps = "ipcOps"; + /// + /// MediaPlayer + /// + public const string MediaPlayer = "mediaPlayer"; + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/TieLine.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/TieLine.cs new file mode 100644 index 00000000..25bf4ea3 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/TieLine.cs @@ -0,0 +1,111 @@ +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 class TieLine + { + public RoutingOutputPort SourcePort { get; private set; } + public RoutingInputPort DestinationPort { get; private set; } + //public int InUseCount { get { return DestinationUsingThis.Count; } } + + /// + /// Gets the type of this tie line. Will either be the type of the desination port + /// or the type of OverrideType when it is set. + /// + public eRoutingSignalType Type + { + get + { + if (OverrideType.HasValue) return OverrideType.Value; + return DestinationPort.Type; + } + } + + /// + /// Use this to override the Type property for the destination port. For example, + /// when the tie line is type AudioVideo, and the signal flow should be limited to + /// Audio-only or Video only, changing this type will alter the signal paths + /// available to the routing algorithm without affecting the actual Type + /// of the destination port. + /// + public eRoutingSignalType? OverrideType { get; set; } + + //List DestinationUsingThis = new List(); + + /// + /// For tie lines that represent internal links, like from cards to the matrix in a DM. + /// This property is true if SourcePort and DestinationPort IsInternal + /// property are both true + /// + public bool IsInternal { get { return SourcePort.IsInternal && DestinationPort.IsInternal; } } + public bool TypeMismatch { get { return SourcePort.Type != DestinationPort.Type; } } + public bool ConnectionTypeMismatch { get { return SourcePort.ConnectionType != DestinationPort.ConnectionType; } } + public string TypeMismatchNote { get; set; } + + /// + /// + /// + /// + /// + public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort) + { + if (sourcePort == null || destinationPort == null) + throw new ArgumentNullException("source or destination port"); + SourcePort = sourcePort; + DestinationPort = destinationPort; + } + + /// + /// Creates a tie line with an overriding Type. See help for OverrideType property for info + /// + /// The signal type to limit the link to. Overrides DestinationPort.Type + public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType overrideType) : + this(sourcePort, destinationPort) + { + OverrideType = overrideType; + } + + /// + /// Will link up video status from supporting inputs to connected outputs + /// + public void Activate() + { + // Now does nothing + } + + public void Deactivate() + { + // Now does nothing + } + + public override string ToString() + { + return string.Format("Tie line: [{0}]{1} --> [{2}]{3}", SourcePort.ParentDevice.Key, SourcePort.Key, + DestinationPort.ParentDevice.Key, DestinationPort.Key); + } + } + + //******************************************************************************** + + public class TieLineCollection : List + { + public static TieLineCollection Default + { + get + { + if (_Default == null) + _Default = new TieLineCollection(); + return _Default; + } + } + static TieLineCollection _Default; + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/TieLineConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/TieLineConfig.cs new file mode 100644 index 00000000..d0087747 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/TieLineConfig.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharpPro; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core.Config +{ + public class TieLineConfig + { + public string SourceKey { get; set; } + public string SourceCard { get; set; } + public string SourcePort { get; set; } + public string DestinationKey { get; set; } + public string DestinationCard { get; set; } + public string DestinationPort { get; set; } + + /// + /// Returns the appropriate tie line for either a card-based device or + /// regular device with ports on-device. + /// + /// null if config data does not match ports, cards or devices + public TieLine GetTieLine() + { + Debug.Console(0, "Build TieLine: {0}", this); + // Get the source device + var sourceDev = DeviceManager.GetDeviceForKey(SourceKey) as IRoutingOutputs; + if (sourceDev == null) + { + LogError("Routable source not found"); + return null; + } + + // Get the destination device + var destDev = DeviceManager.GetDeviceForKey(DestinationKey) as IRoutingInputs; + if (destDev == null) + { + LogError("Routable destination not found"); + return null; + } + + //Get the source port + RoutingOutputPort sourceOutputPort = null; + //// If it's a card-based device, get the card and then the source port + //if (sourceDev is ICardPortsDevice) + //{ + // if (SourceCard == null) + // { + // LogError("Card missing from source device config"); + // return null; + // } + // sourceOutputPort = (sourceDev as ICardPortsDevice).GetChildOutputPort(SourceCard, SourcePort); + // if (sourceOutputPort == null) + // { + // LogError("Source card does not contain port"); + // return null; + // } + //} + //// otherwise it's a normal port device, get the source port + //else + //{ + sourceOutputPort = sourceDev.OutputPorts[SourcePort]; + if (sourceOutputPort == null) + { + LogError("Source does not contain port"); + return null; + } + //} + + + //Get the Destination port + RoutingInputPort destinationInputPort = null; + //// If it's a card-based device, get the card and then the Destination port + //if (destDev is ICardPortsDevice) + //{ + // if (DestinationCard == null) + // { + // LogError("Card missing from destination device config"); + // return null; + // } + // destinationInputPort = (destDev as ICardPortsDevice).GetChildInputPort(DestinationCard, DestinationPort); + // if (destinationInputPort == null) + // { + // LogError("Destination card does not contain port"); + // return null; + // } + //} + //// otherwise it's a normal port device, get the Destination port + //else + //{ + destinationInputPort = destDev.InputPorts[DestinationPort]; + if (destinationInputPort == null) + { + LogError("Destination does not contain port"); + return null; + } + //} + + return new TieLine(sourceOutputPort, destinationInputPort); + } + + void LogError(string msg) + { + Debug.Console(1, "WARNING: Cannot create tie line: {0}:\r {1}", msg, this); + } + + public override string ToString() + { + return string.Format("{0}.{1}.{2} --> {3}.{4}.{5}", SourceKey, SourceCard, SourcePort, + DestinationKey, DestinationCard, DestinationPort); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/Shade Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/Shade Interfaces.cs new file mode 100644 index 00000000..b4d7c88d --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/Shade Interfaces.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.Shades +{ + /// + /// Requirements for an object that contains shades + /// + public interface IShades + { + List Shades { get; } + } + + /// + /// Requirements for a device that implements basic Open/Close shade control + /// + public interface IShadesOpenClose + { + void Open(); + void Close(); + } + + /// + /// Requirements for a device that implements basic Open/Close/Stop shade control (Uses 3 relays) + /// + public interface IShadesOpenCloseStop : IShadesOpenClose + { + void StopOrPreset(); + } + + /// + /// Requirements for a shade that implements press/hold raise/lower functions + /// + public interface IShadesRaiseLower + { + void Raise(bool state); + void Lower(bool state); + } + + /// + /// Requirements for a shade device that provides raising/lowering feedback + /// + public interface IShadesRaiseLowerFeedback + { + BoolFeedback ShadeIsLoweringFeedback { get; } + BoolFeedback ShadeIsRaisingFeedback { get; } + } + + /// + /// Requirements for a shade/scene that is open or closed + /// + public interface IShadesOpenClosedFeedback: IShadesOpenClose + { + BoolFeedback ShadeIsOpenFeedback { get; } + BoolFeedback ShadeIsClosedFeedback { get; } + } + + /// + /// + /// + public interface IShadesStop + { + void Stop(); + } + + /// + /// + /// + public interface IShadesStopOrMove + { + void OpenOrStop(); + void CloseOrStop(); + void OpenCloseOrStop(); + } + + /// + /// Basic feedback for shades/scene stopped + /// + public interface IShadesStopFeedback + { + BoolFeedback IsStoppedFeedback { get; } + } + + /// + /// Requirements for position + /// + public interface IShadesPosition + { + void SetPosition(ushort value); + } + + /// + /// Basic feedback for shades position + /// + public interface IShadesFeedback: IShadesPosition, IShadesStopFeedback + { + IntFeedback PositionFeedback { get; } + } + + /// + /// + /// + public interface ISceneFeedback + { + void Run(); + BoolFeedback AllAreAtSceneFeedback { get; } + } + + public interface ICrestronBasicShade : IShadesOpenClosedFeedback, IShadesStop, + IShadesStopOrMove, IShadesFeedback, IShadesRaiseLowerFeedback + { + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/ShadeBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/ShadeBase.cs new file mode 100644 index 00000000..636a5440 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/ShadeBase.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core.CrestronIO; + +namespace PepperDash.Essentials.Core.Shades +{ + /// + /// Base class for a shade device + /// + public abstract class ShadeBase : Device, IShadesOpenClose + { + public ShadeBase(string key, string name) + : base(key, name) + { + + } + + #region iShadesOpenClose Members + + public abstract void Open(); + public abstract void StopOrPreset(); + public abstract void Close(); + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/ShadeController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/ShadeController.cs new file mode 100644 index 00000000..b226af05 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Shades/ShadeController.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.Shades +{ + /// + /// Class that contains the shades to be controlled in a room + /// + public class ShadeController : Device, IShades + { + ShadeControllerConfigProperties Config; + + public List Shades { get; private set; } + + public ShadeController(string key, string name, ShadeControllerConfigProperties config) + : base(key, name) + { + Config = config; + + Shades = new List(); + } + + public override bool CustomActivate() + { + foreach (var shadeConfig in Config.Shades) + { + var shade = DeviceManager.GetDeviceForKey(shadeConfig.Key) as ShadeBase; + + if (shade != null) + { + AddShade(shade); + } + } + return base.CustomActivate(); + } + + void AddShade(ShadeBase shade) + { + Shades.Add(shade); + } + } + + public class ShadeControllerConfigProperties + { + public List Shades { get; set; } + + + public class ShadeConfig + { + public string Key { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/SigHelper.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SigHelper.cs new file mode 100644 index 00000000..86395114 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SigHelper.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// Helper class for various Sig events + /// + public class SigHelper + { + /// + /// Runs action when Sig is pressed + /// + /// + public static void Pressed(Sig sig, Action act) { if (sig.BoolValue) act(); } + + /// + /// Runs action when Sig is released + /// + public static void Released(Sig sig, Action act) { if (!sig.BoolValue) act(); } + + /// + /// Safely sets an action to non-null sig + /// + public static void SetBoolOutAction(BoolOutputSig sig, Action a) + { + if (sig != null) + sig.UserObject = a; + } + + /// + /// Safely clears action of non-null sig. + /// + public static void ClearBoolOutAction(BoolOutputSig sig) + { + if (sig != null) + sig.UserObject = null; + } + + /// + /// Does a timed ramp, where the time is scaled proportional to the + /// remaining range to cover + /// + /// Ushort sig to scale + /// Level to go to + /// In ms (not hundredths like Crestron Sig ramp function) + public static void RampTimeScaled(Sig sig, ushort newLevel, uint time) + { + ushort level = sig.UShortValue; + int diff = Math.Abs(level - newLevel); + uint scaledTime = (uint)(diff * time / 65535); + Ramp(sig, newLevel, scaledTime); + } + + /// + /// Ramps signal + /// + /// + /// + /// In ms (not hundredths like Crestron Sig ramp function) + public static void Ramp(Sig sig, ushort level, uint time) + { + sig.CreateRamp(level, time / 10); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectDPad.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectDPad.cs new file mode 100644 index 00000000..2307be7e --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectDPad.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +namespace PepperDash.Essentials.Core.SmartObjects +{ + public class SmartObjectDPad : SmartObjectHelperBase + { + public BoolOutputSig SigUp { get { return GetBoolOutputNamed("Up"); } } + public BoolOutputSig SigDown { get { return GetBoolOutputNamed("Down"); } } + public BoolOutputSig SigLeft { get { return GetBoolOutputNamed("Left"); } } + public BoolOutputSig SigRight { get { return GetBoolOutputNamed("Right"); } } + public BoolOutputSig SigCenter { get { return GetBoolOutputNamed("Center"); } } + + public SmartObjectDPad(SmartObject so, bool useUserObjectHandler) + : base(so, useUserObjectHandler) + { + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectDynamicList.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectDynamicList.cs new file mode 100644 index 00000000..bf73ba1b --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectDynamicList.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core.SmartObjects +{ + public class SmartObjectDynamicList : SmartObjectHelperBase + { + public const string SigNameScrollToItem = "Scroll To Item"; + public const string SigNameSetNumberOfItems = "Set Number of Items"; + + public uint NameSigOffset { get; private set; } + + public ushort Count + { + get + { + return SmartObject.UShortInput[SigNameSetNumberOfItems].UShortValue; + } + set { SmartObject.UShortInput[SigNameSetNumberOfItems].UShortValue = value; } + } + + /// + /// The limit of the list object, as defined by VTPro settings. Zero if object + /// is not a list + /// + public int MaxCount { get; private set; } + + /// + /// Wrapper for smart object + /// + /// + /// True if the standard user object action handler will be used + /// The starting join of the string sigs for the button labels + public SmartObjectDynamicList(SmartObject so, bool useUserObjectHandler, uint nameSigOffset) : base(so, useUserObjectHandler) + { + try + { + // Just try to touch the count signal to make sure this is indeed a dynamic list + var c = Count; + NameSigOffset = nameSigOffset; + MaxCount = SmartObject.BooleanOutput.Count(s => s.Name.EndsWith("Pressed")); + //Debug.Console(2, "Smart object {0} has {1} max", so.ID, MaxCount); + } + catch + { + var msg = string.Format("SmartObjectDynamicList: Smart Object {0:X2}-{1} is not a dynamic list. Ignoring", so.Device.ID, so.ID); + Debug.Console(0, Debug.ErrorLogLevel.Error, msg); + } + } + + /// + /// Builds a new list item + /// + public void SetItem(uint index, string mainText, string iconName, Action action) + { + SetItemMainText(index, mainText); + SetItemIcon(index, iconName); + SetItemButtonAction(index, action); + //try + //{ + // SetMainButtonText(index, text); + // SetIcon(index, iconName); + // SetButtonAction(index, action); + //} + //catch(Exception e) + //{ + // Debug.Console(1, "Cannot set Dynamic List item {0} on smart object {1}", index, SmartObject.ID); + // ErrorLog.Warn(e.ToString()); + //} + } + + public void SetItemMainText(uint index, string text) + { + if (index > MaxCount) return; + // The list item template defines CIPS tags that refer to standard joins + (SmartObject.Device as BasicTriList).StringInput[NameSigOffset + index].StringValue = text; + } + + public void SetItemIcon(uint index, string iconName) + { + if (index > MaxCount) return; + SmartObject.StringInput[string.Format("Set Item {0} Icon Serial", index)].StringValue = iconName; + } + + public void SetItemButtonAction(uint index, Action action) + { + if (index > MaxCount) return; + SmartObject.BooleanOutput[string.Format("Item {0} Pressed", index)].UserObject = action; + } + + /// + /// Sets the feedback on the given line, clearing others when interlocked is set + /// + public void SetFeedback(uint index, bool interlocked) + { + if (interlocked) + ClearFeedbacks(); + SmartObject.BooleanInput[string.Format("Item {0} Selected", index)].BoolValue = true; + } + + /// + /// Clears all button feedbacks + /// + public void ClearFeedbacks() + { + for(int i = 1; i<= Count; i++) + SmartObject.BooleanInput[string.Format("Item {0} Selected", i)].BoolValue = false; + } + + /// + /// Removes Action object from all buttons + /// + public void ClearActions() + { + Debug.Console(2, "SO CLEAR"); + for(ushort i = 1; i <= MaxCount; i++) + SmartObject.BooleanOutput[string.Format("Item {0} Pressed", i)].UserObject = null; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectHelper.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectHelper.cs new file mode 100644 index 00000000..f30d8da1 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectHelper.cs @@ -0,0 +1,128 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + public class SmartObjectHelper + { + public static uint GetSmartObjectJoinForTypeAndObject(uint sourceType, uint typeOffset) + { + return (uint)(10000 + (sourceType * 100) + typeOffset); + } + + //public static void DumpSmartObject(SmartGraphicsTouchpanelControllerBase tp, uint id) + //{ + // if (!tp.TriList.SmartObjects.Contains(id)) + // { + // Debug.Console(0, tp, "does not contain smart object ID {0}", id); + // return; + // } + // var so = tp.TriList.SmartObjects[id]; + // Debug.Console(0, tp, "Signals for smart object ID {0}", id); + // Debug.Console(0, "BooleanInput -------------------------------"); + // foreach (var s in so.BooleanInput) + // Debug.Console(0, " {0,5} {1}", s.Number, s.Name); + // Debug.Console(0, "UShortInput -------------------------------"); + // foreach (var s in so.UShortInput) + // Debug.Console(0, " {0,5} {1}", s.Number, s.Name); + // Debug.Console(0, "StringInput -------------------------------"); + // foreach (var s in so.StringInput) + // Debug.Console(0, " {0,5} {1}", s.Number, s.Name); + // Debug.Console(0, "BooleanOutput -------------------------------"); + // foreach (var s in so.BooleanOutput) + // Debug.Console(0, " {0,5} {1}", s.Number, s.Name); + // Debug.Console(0, "UShortOutput -------------------------------"); + // foreach (var s in so.UShortOutput) + // Debug.Console(0, " {0,5} {1}", s.Number, s.Name); + // Debug.Console(0, "StringOutput -------------------------------"); + // foreach (var s in so.StringOutput) + // Debug.Console(0, " {0,5} {1}", s.Number, s.Name); + //} + + ///// + ///// Inserts/removes the appropriate UO's onto sigs + ///// + ///// + ///// + ///// + ///// + //public static void LinkNumpadWithUserObjects(BasicTriListWithSmartObject triList, + // uint smartObjectId, List deviceUserObjects, Cue Misc_1Function, Cue Misc_2Function, bool state) + //{ + // var sigDict = new Dictionary + // { + // { "0", CommonBoolCue.Digit0 }, + // { "1", CommonBoolCue.Digit1 }, + // { "2", CommonBoolCue.Digit2 }, + // { "3", CommonBoolCue.Digit3 }, + // { "4", CommonBoolCue.Digit4 }, + // { "5", CommonBoolCue.Digit5 }, + // { "6", CommonBoolCue.Digit6 }, + // { "7", CommonBoolCue.Digit7 }, + // { "8", CommonBoolCue.Digit8 }, + // { "9", CommonBoolCue.Digit9 }, + // { "Misc_1", Misc_1Function }, + // { "Misc_2", Misc_2Function }, + // }; + // LinkSmartObjectWithUserObjects(triList, smartObjectId, deviceUserObjects, sigDict, state); + //} + + //public static void LinkDpadWithUserObjects(BasicTriListWithSmartObject triList, + // uint smartObjectId, List deviceUserObjects, bool state) + //{ + // var sigDict = new Dictionary + // { + // { "Up", CommonBoolCue.Up }, + // { "Down", CommonBoolCue.Down }, + // { "Left", CommonBoolCue.Left }, + // { "Right", CommonBoolCue.Right }, + // { "OK", CommonBoolCue.Select }, + // }; + // LinkSmartObjectWithUserObjects(triList, smartObjectId, deviceUserObjects, sigDict, state); + //} + + + ///// + ///// MOVE TO HELPER CLASS + ///// + ///// + ///// + ///// + ///// + ///// + //public static void LinkSmartObjectWithUserObjects(BasicTriListWithSmartObject triList, + // uint smartObjectId, List deviceUserObjects, Dictionary smartObjectSigMap, bool state) + //{ + // // Hook up smart objects if applicable + // if (triList.SmartObjects.Contains(smartObjectId)) + // { + // var smartObject = triList.SmartObjects[smartObjectId]; + // foreach (var kvp in smartObjectSigMap) + // { + // if (smartObject.BooleanOutput.Contains(kvp.Key)) + // { + // if (state) + // { + // // look for a user object and if so, attach/detach it to/from the sig. + // var uo = deviceUserObjects.FirstOrDefault(ao => ao.Cue == kvp.Value); + // if (uo != null) + // smartObject.BooleanOutput[kvp.Key].UserObject = uo; + // } + // else + // smartObject.BooleanOutput[kvp.Key].UserObject = null; + // } + // else + // Debug.Console(0, "Smart object {0} does not contain Sig {1}", smartObject.ID, kvp.Key); + // } + // } + // else + // Debug.Console(0, "ERROR Smart object {0} not found on {1:X2}", smartObjectId, triList.ID); + //} + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectHelperBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectHelperBase.cs new file mode 100644 index 00000000..6fadf491 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectHelperBase.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.SmartObjects +{ + public class SmartObjectHelperBase + { + public SmartObject SmartObject { get; private set; } + + /// + /// This should be set by all inheriting classes, after the class has verified that it is linked to the right object. + /// + public bool Validated { get; protected set; } + + public SmartObjectHelperBase(SmartObject so, bool useUserObjectHandler) + { + SmartObject = so; + if (useUserObjectHandler) + { + // Prevent this from double-registering + SmartObject.SigChange -= this.SmartObject_SigChange; + SmartObject.SigChange += this.SmartObject_SigChange; + } + } + + ~SmartObjectHelperBase() + { + SmartObject.SigChange -= this.SmartObject_SigChange; + } + + /// + /// Helper to get a sig name with debugging when fail + /// + /// + /// + public BoolOutputSig GetBoolOutputNamed(string name) + { + if (SmartObject.BooleanOutput.Contains(name)) + return SmartObject.BooleanOutput[name]; + else + Debug.Console(0, "WARNING: Cannot get signal. Smart object {0} on trilist {1:x2} does not contain signal '{2}'", + SmartObject.ID, SmartObject.Device.ID, name); + return null; + } + + /// + /// Sets action on signal after checking for existence. + /// + /// + /// + public void SetBoolAction(string name, Action a) + { + if (SmartObject.BooleanOutput.Contains(name)) + SmartObject.BooleanOutput[name].UserObject = a; + else + { + Debug.Console(0, "WARNING: Cannot set action. Smart object {0} on trilist {1:x2} does not contain signal '{2}'", + SmartObject.ID, SmartObject.Device.ID, name); + } + } + + /// + /// Standard Action listener + /// + /// + /// + void SmartObject_SigChange(GenericBase currentDevice, SmartObjectEventArgs args) + { + var uo = args.Sig.UserObject; + if (uo is Action) + (uo as Action)(args.Sig.BoolValue); + else if (uo is Action) + (uo as Action)(args.Sig.UShortValue); + else if (uo is Action) + (uo as Action)(args.Sig.StringValue); + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectNumeric.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectNumeric.cs new file mode 100644 index 00000000..7e574242 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SmartObjectNumeric.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +namespace PepperDash.Essentials.Core.SmartObjects +{ + public class SmartObjectNumeric : SmartObjectHelperBase + { + /// + /// Defaults to "Misc_1". The name of the button in VTPro (Usually the text) + /// + public string Misc1SigName { get; set; } + /// + /// Defaults to "Misc_2". The name of the button in VTPro (Usually the text) + /// + public string Misc2SigName { get; set; } + + public BoolOutputSig Digit1 { get { return GetBoolOutputNamed("1"); } } + public BoolOutputSig Digit2 { get { return GetBoolOutputNamed("2"); } } + public BoolOutputSig Digit3 { get { return GetBoolOutputNamed("3"); } } + public BoolOutputSig Digit4 { get { return GetBoolOutputNamed("4"); } } + public BoolOutputSig Digit5 { get { return GetBoolOutputNamed("5"); } } + public BoolOutputSig Digit6 { get { return GetBoolOutputNamed("6"); } } + public BoolOutputSig Digit7 { get { return GetBoolOutputNamed("7"); } } + public BoolOutputSig Digit8 { get { return GetBoolOutputNamed("8"); } } + public BoolOutputSig Digit9 { get { return GetBoolOutputNamed("9"); } } + public BoolOutputSig Digit0 { get { return GetBoolOutputNamed("0"); } } + public BoolOutputSig Misc1 { get { return GetBoolOutputNamed(Misc1SigName); } } + public BoolOutputSig Misc2 { get { return GetBoolOutputNamed(Misc2SigName); } } + + public SmartObjectNumeric(SmartObject so, bool useUserObjectHandler) : base(so, useUserObjectHandler) + { + Misc1SigName = "Misc_1"; + Misc2SigName = "Misc_2"; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SubpageReferencList/SourceListSubpageReferenceList.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SubpageReferencList/SourceListSubpageReferenceList.cs new file mode 100644 index 00000000..e8c49b40 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SubpageReferencList/SourceListSubpageReferenceList.cs @@ -0,0 +1,167 @@ + + + +//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.UI; + +//using PepperDash.Core; + + +//namespace PepperDash.Essentials.Core +//{ + +// //***************************************************************************** +// /// +// /// Wrapper class for subpage reference list. Contains helpful methods to get at the various signal groupings +// /// and to get individual signals using an index and a join. +// /// +// public class SourceListSubpageReferenceList : SubpageReferenceList +// { +// public const uint SmartObjectJoin = 3801; + +// Action SourceSelectCallback; + +// EssentialsRoom CurrentRoom; + +// public SourceListSubpageReferenceList(BasicTriListWithSmartObject tl, +// Action sourceSelectCallback) +// : base(tl, SmartObjectJoin, 3, 1, 3) +// { +// SourceSelectCallback = sourceSelectCallback; +// } + +// void SetSourceList(Dictionary dict) +// { +// // Iterate all positions, including ones missing from the dict. +// var max = dict.Keys.Max(); +// for (uint i = 1; i <= max; i++) +// { +// // Add the source if it's in the dict +// if (dict.ContainsKey(i)) +// { +// Items.Add(new SourceListSubpageReferenceListItem(i, dict[i], this, SourceSelectCallback)); +// // Plug the callback function into the buttons +// } +// // Blank the line +// else +// Items.Add(new SourceListSubpageReferenceListItem(i, null, +// this, SourceSelectCallback)); +// } +// Count = (ushort)max; +// } + +// /// +// /// Links the SRL to the Room's PresentationSourceChange event for updating of the UI +// /// +// /// +// public void AttachToRoom(EssentialsRoom room) +// { +// CurrentRoom = room; +// SetSourceList(room.Sources); +// CurrentRoom.PresentationSourceChange -= CurrentRoom_PresentationSourceChange; +// CurrentRoom.PresentationSourceChange += CurrentRoom_PresentationSourceChange; +// SetPresentationSourceFb(CurrentRoom.CurrentPresentationSource); +// } + +// /// +// /// Disconnects the SRL from a Room's PresentationSourceChange +// /// +// public void DetachFromCurrentRoom() +// { +// ClearPresentationSourceFb(CurrentRoom.CurrentPresentationSource); +// if(CurrentRoom != null) +// CurrentRoom.PresentationSourceChange -= CurrentRoom_PresentationSourceChange; +// CurrentRoom = null; +// } + +// // Handler to route source changes into list feedback +// void CurrentRoom_PresentationSourceChange(object sender, EssentialsRoomSourceChangeEventArgs args) +// { +// Debug.Console(2, "SRL received source change"); +// ClearPresentationSourceFb(args.OldSource); +// SetPresentationSourceFb(args.NewSource); +// } + +// void ClearPresentationSourceFb(IPresentationSource source) +// { +// if (source == null) return; +// var oldSourceItem = (SourceListSubpageReferenceListItem)Items.FirstOrDefault( +// i => ((SourceListSubpageReferenceListItem)i).SourceDevice == source); +// if (oldSourceItem != null) +// oldSourceItem.ClearFeedback(); +// } + +// void SetPresentationSourceFb(IPresentationSource source) +// { +// if (source == null) return; +// // Now set the new source to light up +// var newSourceItem = (SourceListSubpageReferenceListItem)Items.FirstOrDefault( +// i => ((SourceListSubpageReferenceListItem)i).SourceDevice == source); +// if (newSourceItem != null) +// newSourceItem.SetFeedback(); +// } +// } + +// public class SourceListSubpageReferenceListItem : SubpageReferenceListItem +// { +// public readonly IPresentationSource SourceDevice; + +// public const uint ButtonPressJoin = 1; +// public const uint SelectedFeedbackJoin = 2; +// public const uint ButtonTextJoin = 1; +// public const uint IconNameJoin = 2; + +// public SourceListSubpageReferenceListItem(uint index, +// IPresentationSource srcDevice, SubpageReferenceList owner, Action sourceSelectCallback) +// : base(index, owner) +// { +// if (srcDevice == null) throw new ArgumentNullException("srcDevice"); +// if (owner == null) throw new ArgumentNullException("owner"); +// if (sourceSelectCallback == null) throw new ArgumentNullException("sourceSelectCallback"); + + +// SourceDevice = srcDevice; +// var nameSig = owner.StringInputSig(index, ButtonTextJoin); +// // Should be able to see if there is not enough buttons right here +// if (nameSig == null) +// { +// Debug.Console(0, "ERROR: Item {0} does not exist on source list SRL", index); +// return; +// } +// nameSig.StringValue = srcDevice.Name; +// owner.StringInputSig(index, IconNameJoin).StringValue = srcDevice.IconName; + +// // Assign a source selection action to the appropriate button's UserObject - on release +// owner.GetBoolFeedbackSig(index, ButtonPressJoin).UserObject = new Action(b => +// { if (!b) sourceSelectCallback(index); }); + +// // hook up the video icon +// var videoDev = srcDevice as IAttachVideoStatus; +// if (videoDev != null) +// { +// var status = videoDev.GetVideoStatuses(); +// if (status != null) +// { +// Debug.Console(1, "Linking {0} video status to SRL", videoDev.Key); +// videoDev.GetVideoStatuses().VideoSyncFeedback.LinkInputSig(owner.BoolInputSig(index, 3)); +// } +// } +// } + +// public void SetFeedback() +// { +// Owner.BoolInputSig(Index, SelectedFeedbackJoin).BoolValue = true; +// } + +// public void ClearFeedback() +// { +// Owner.BoolInputSig(Index, SelectedFeedbackJoin).BoolValue = false; +// } +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SubpageReferencList/SubpageReferenceList.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SubpageReferencList/SubpageReferenceList.cs new file mode 100644 index 00000000..2f8794f0 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SubpageReferencList/SubpageReferenceList.cs @@ -0,0 +1,263 @@ + +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.UI; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Core +{ + ////***************************************************************************** + ///// + ///// Base class for all subpage reference list controllers + ///// + //public class SubpageReferenceListController + //{ + // public SubpageReferenceList TheList { get; protected set; } + //} + + //***************************************************************************** + /// + /// Wrapper class for subpage reference list. Contains helpful methods to get at the various signal groupings + /// and to get individual signals using an index and a join. + /// + public class SubpageReferenceList + { + + public ushort Count + { + get { return SetNumberOfItemsSig.UShortValue; } + set { SetNumberOfItemsSig.UShortValue = value; } + } + public ushort MaxDefinedItems { get; private set; } + + public UShortInputSig ScrollToItemSig { get; private set; } + UShortInputSig SetNumberOfItemsSig; + public uint BoolIncrement { get; protected set; } + public uint UShortIncrement { get; protected set; } + public uint StringIncrement { get; protected set; } + + protected readonly SmartObject SRL; + protected readonly List Items = new List(); + + public SubpageReferenceList(BasicTriListWithSmartObject triList, uint smartObjectId, + uint boolIncrement, uint ushortIncrement, uint stringIncrement) + { + SmartObject obj; + // Fail cleanly if not defined + if (triList.SmartObjects == null || triList.SmartObjects.Count == 0) + { + Debug.Console(0, "TriList {0:X2} Smart objects have not been loaded", triList.ID, smartObjectId); + return; + } + if (triList.SmartObjects.TryGetValue(smartObjectId, out obj)) + { + SRL = triList.SmartObjects[smartObjectId]; + ScrollToItemSig = SRL.UShortInput["Scroll To Item"]; + SetNumberOfItemsSig = SRL.UShortInput["Set Number of Items"]; + BoolIncrement = boolIncrement; + UShortIncrement = ushortIncrement; + StringIncrement = stringIncrement; + + // Count the enable lines to see what max items is + MaxDefinedItems = (ushort)SRL.BooleanInput + .Where(s => s.Name.Contains("Enable")).Count(); + Debug.Console(2, "SRL {0} contains max {1} items", SRL.ID, MaxDefinedItems); + + SRL.SigChange -= new SmartObjectSigChangeEventHandler(SRL_SigChange); + SRL.SigChange += new SmartObjectSigChangeEventHandler(SRL_SigChange); + } + else + Debug.Console(0, "ERROR: TriList 0x{0:X2} Cannot load smart object {1}. Verify correct SGD file is loaded", + triList.ID, smartObjectId); + } + + /// + /// Adds item to saved list of displayed items (not necessarily in order) + /// DOES NOT adjust Count + /// + /// + public void AddItem(SubpageReferenceListItem item) + { + Items.Add(item); + } + + /// + /// Items need to be responsible for managing their own deallocation process, + /// disconnecting from events, etc. + /// + /// + public void Clear() + { + // If a line item needs to disconnect an CueActionPair or do something to release RAM + foreach (var item in Items) item.Clear(); + // Empty the list + Items.Clear(); + // Clean up the SRL + Count = 0; + ScrollToItemSig.UShortValue = 1; + } + + /// + /// Optional call to refresh the signals on the objects in the SRL - this calls Refresh() on + /// all SubpageReferenceListItem items + /// + public void Refresh() + { + foreach (var item in Items) item.Refresh(); + } + + + // Helpers to get sigs by their weird SO names + + /// + /// Returns the Sig associated with a given SRL line index + /// and the join number of the object on the SRL subpage. + /// Note: If the join number exceeds the increment range, or the count of Sigs on the + /// list object, this will return null + /// + /// The line or item position on the SRL + /// The join number of the item on the SRL subpage + /// A Sig or null if the numbers are out of range + public BoolOutputSig GetBoolFeedbackSig(uint index, uint sigNum) + { + if (sigNum > BoolIncrement) return null; + return SRL.BooleanOutput.FirstOrDefault(s => s.Name.Equals(GetBoolFeedbackSigName(index, sigNum))); + } + + /// + /// Returns the Sig associated with a given SRL line index + /// and the join number of the object on the SRL subpage. + /// Note: If the join number exceeds the increment range, or the count of Sigs on the + /// list object, this will return null + /// + /// The line or item position on the SRL + /// The join number of the item on the SRL subpage + /// A Sig or null if the numbers are out of range + public UShortOutputSig GetUShortOutputSig(uint index, uint sigNum) + { + if (sigNum > UShortIncrement) return null; + return SRL.UShortOutput.FirstOrDefault(s => s.Name.Equals(GetBoolFeedbackSigName(index, sigNum))); + } + + /// + /// Returns the Sig associated with a given SRL line index + /// and the join number of the object on the SRL subpage. + /// Note: If the join number exceeds the increment range, or the count of Sigs on the + /// list object, this will return null + /// + /// The line or item position on the SRL + /// The join number of the item on the SRL subpage + /// A Sig or null if the numbers are out of range + public StringOutputSig GetStringOutputSig(uint index, uint sigNum) + { + if (sigNum > StringIncrement) return null; + return SRL.StringOutput.FirstOrDefault(s => s.Name.Equals(GetBoolFeedbackSigName(index, sigNum))); + } + + /// + /// Returns the Sig associated with a given SRL line index + /// and the join number of the object on the SRL subpage. + /// Note: If the join number exceeds the increment range, or the count of Sigs on the + /// list object, this will return null + /// + /// The line on the SRL + /// The join number of the item on the SRL subpage + /// A Sig or null if the numbers are out of range + public BoolInputSig BoolInputSig(uint index, uint sigNum) + { + if (sigNum > BoolIncrement) return null; + return SRL.BooleanInput.FirstOrDefault(s => s.Name.Equals(GetBoolInputSigName(index, sigNum))); + } + + /// + /// Returns the Sig associated with a given SRL line index + /// and the join number of the object on the SRL subpage. + /// Note: If the join number exceeds the increment range, or the count of Sigs on the + /// list object, this will return null + /// + /// The line on the SRL + /// The join number of the item on the SRL subpage + /// A Sig or null if the numbers are out of range + public UShortInputSig UShortInputSig(uint index, uint sigNum) + { + if (sigNum > UShortIncrement) return null; + return SRL.UShortInput.FirstOrDefault(s => s.Name.Equals(GetUShortInputSigName(index, sigNum))); + } + + /// + /// Returns the Sig associated with a given SRL line index + /// and the join number of the object on the SRL subpage. + /// Note: If the join number exceeds the increment range, or the count of Sigs on the + /// list object, this will return null + /// + /// The line on the SRL + /// The join number of the item on the SRL subpage + /// A Sig or null if the numbers are out of range + public StringInputSig StringInputSig(uint index, uint sigNum) + { + if (sigNum > StringIncrement) return null; + return SRL.StringInput.FirstOrDefault(s => s.Name.Equals(GetStringInputSigName(index, sigNum))); + } + + // Helpers to get signal names + + string GetBoolFeedbackSigName(uint index, uint sigNum) + { + var num = (index - 1) * BoolIncrement + sigNum; + return String.Format("press{0}", num); + } + + string GetUShortOutputSigName(uint index, uint sigNum) + { + var num = (index - 1) * UShortIncrement + sigNum; + return String.Format("an_act{0}", num); + } + + string GetStringOutputSigName(uint index, uint sigNum) + { + var num = (index - 1) * StringIncrement + sigNum; + return String.Format("text-i{0}", num); + } + + string GetBoolInputSigName(uint index, uint sigNum) + { + var num = (index - 1) * BoolIncrement + sigNum; + return String.Format("fb{0}", num); + } + + string GetUShortInputSigName(uint index, uint sigNum) + { + var num = (index - 1) * UShortIncrement + sigNum; + return String.Format("an_fb{0}", num); + } + + string GetStringInputSigName(uint index, uint sigNum) + { + var num = (index - 1) * StringIncrement + sigNum; + return String.Format("text-o{0}", num); + } + + /// + /// Stock SigChange handler + /// + /// + /// + public static void SRL_SigChange(GenericBase currentDevice, SmartObjectEventArgs args) + { + var uo = args.Sig.UserObject; + if (uo is Action) + (uo as Action)(args.Sig.BoolValue); + else if (uo is Action) + (uo as Action)(args.Sig.UShortValue); + else if (uo is Action) + (uo as Action)(args.Sig.StringValue); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SubpageReferencList/SubpageReferenceListItem.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SubpageReferencList/SubpageReferenceListItem.cs new file mode 100644 index 00000000..30e15f74 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/SmartObjects/SubpageReferencList/SubpageReferenceListItem.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +namespace PepperDash.Essentials.Core +{ + public class SubpageReferenceListItem + { + /// + /// The list that this lives in + /// + protected SubpageReferenceList Owner; + protected uint Index; + + public SubpageReferenceListItem(uint index, SubpageReferenceList owner) + { + Index = index; + Owner = owner; + } + + /// + /// Called by SRL to release all referenced objects + /// + public virtual void Clear() + { + } + + public virtual void Refresh() { } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Timers/CountdownTimer.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Timers/CountdownTimer.cs new file mode 100644 index 00000000..a8da97e4 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Timers/CountdownTimer.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public class SecondsCountdownTimer: IKeyed + { + public event EventHandler HasStarted; + public event EventHandler HasFinished; + public event EventHandler WasCancelled; + + public string Key { get; private set; } + + public BoolFeedback IsRunningFeedback { get; private set; } + bool _IsRunning; + + public IntFeedback PercentFeedback { get; private set; } + public StringFeedback TimeRemainingFeedback { get; private set; } + + public bool CountsDown { get; set; } + public int SecondsToCount { get; set; } + + public DateTime StartTime { get; private set; } + public DateTime FinishTime { get; private set; } + + CTimer SecondTimer; + + /// + /// + /// + /// + public SecondsCountdownTimer(string key) + { + Key = key; + IsRunningFeedback = new BoolFeedback(() => _IsRunning); + + TimeRemainingFeedback = new StringFeedback(() => + { + // Need to handle up and down here. + + if (StartTime == null || FinishTime == null) + return ""; + var timeSpan = FinishTime - DateTime.Now; + return Math.Round(timeSpan.TotalSeconds).ToString(); + }); + + PercentFeedback = new IntFeedback(() => + { + if (StartTime == null || FinishTime == null) + return 0; + double percent = (FinishTime - DateTime.Now).TotalSeconds + / (FinishTime - StartTime).TotalSeconds + * 100; + return (int)percent; + }); + } + + /// + /// + /// + public void Start() + { + if (_IsRunning) + return; + StartTime = DateTime.Now; + FinishTime = StartTime + TimeSpan.FromSeconds(SecondsToCount); + + if (SecondTimer != null) + SecondTimer.Stop(); + SecondTimer = new CTimer(SecondElapsedTimerCallback, null, 0, 1000); + _IsRunning = true; + IsRunningFeedback.FireUpdate(); + + var handler = HasStarted; + if (handler != null) + handler(this, new EventArgs()); + } + + /// + /// + /// + public void Reset() + { + _IsRunning = false; + Start(); + } + + /// + /// + /// + public void Cancel() + { + StopHelper(); + + var handler = WasCancelled; + if (handler != null) + handler(this, new EventArgs()); + } + + /// + /// Called upon expiration, or calling this will force timer to finish. + /// + public void Finish() + { + StopHelper(); + + var handler = HasFinished; + if (handler != null) + handler(this, new EventArgs()); + } + + void StopHelper() + { + if (SecondTimer != null) + SecondTimer.Stop(); + _IsRunning = false; + IsRunningFeedback.FireUpdate(); + } + + void SecondElapsedTimerCallback(object o) + { + PercentFeedback.FireUpdate(); + TimeRemainingFeedback.FireUpdate(); + + if (DateTime.Now >= FinishTime) + Finish(); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/CrestronTouchpanelPropertiesConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/CrestronTouchpanelPropertiesConfig.cs new file mode 100644 index 00000000..42882a51 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/CrestronTouchpanelPropertiesConfig.cs @@ -0,0 +1,48 @@ +namespace PepperDash.Essentials.Core +{ + public class CrestronTouchpanelPropertiesConfig + { + public string IpId { get; set; } + public string DefaultRoomKey { get; set; } + public string RoomListKey { get; set; } + public string SgdFile { get; set; } + public string ProjectName { get; set; } + public bool ShowVolumeGauge { get; set; } + public bool UsesSplashPage { get; set; } + public bool ShowDate { get; set; } + public bool ShowTime { get; set; } + public UiSetupPropertiesConfig Setup { get; set; } + public string HeaderStyle { get; set; } + public bool IncludeInFusionRoomHealth { get; set; } + + + /// + /// The count of sources that will trigger the "additional" arrows to show on the SRL. + /// Defaults to 5 + /// + public int SourcesOverflowCount { get; set; } + + public CrestronTouchpanelPropertiesConfig() + { + SourcesOverflowCount = 5; + HeaderStyle = CrestronTouchpanelPropertiesConfig.Habanero; + } + + /// + /// "habanero" + /// + public const string Habanero = "habanero"; + /// + /// "verbose" + /// + public const string Verbose = "verbose"; + } + + /// + /// + /// + public class UiSetupPropertiesConfig + { + public bool IsVisible { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/Keyboards/HabaneroKeyboardController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/Keyboards/HabaneroKeyboardController.cs new file mode 100644 index 00000000..5e068c12 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/Keyboards/HabaneroKeyboardController.cs @@ -0,0 +1,436 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; + +namespace PepperDash.Essentials.Core.Touchpanels.Keyboards +{ + public class HabaneroKeyboardController + { + /// + /// Single-key press events, rather than using a built-up text string on the OutputFeedback + /// + public event EventHandler KeyPress; + + public BasicTriList TriList { get; private set; } + + public StringFeedback OutputFeedback { get; private set; } + + public bool IsVisible { get; private set; } + + public string DotComButtonString { get; set; } + + public string GoButtonText { get; set; } + + public string SecondaryButtonText { get; set; } + + public bool GoButtonVisible { get; set; } + + public bool SecondaryButtonVisible { get; set; } + + int ShiftMode = 0; + + StringBuilder Output; + + public Action HideAction { get; set; } + + CTimer BackspaceTimer; + + /// + /// + /// + /// + public HabaneroKeyboardController(BasicTriList trilist) + { + TriList = trilist; + Output = new StringBuilder(); + OutputFeedback = new StringFeedback(() => Output.ToString()); + DotComButtonString = ".com"; + } + + /// + /// Shows the keyboard and attaches sig handlers in the range of 2901-2969 + /// + public void Show() + { + if (IsVisible) + return; + + TriList.SetSigTrueAction(ClosePressJoin, Hide); + TriList.SetSigTrueAction(GoButtonPressJoin, () => OnKeyPress(KeyboardSpecialKey.GoButton)); + TriList.SetSigTrueAction(SecondaryButtonPressJoin, () => OnKeyPress(KeyboardSpecialKey.SecondaryButton)); + TriList.SetSigTrueAction(2921, () => Press(A(ShiftMode))); + TriList.SetSigTrueAction(2922, () => Press(B(ShiftMode))); + TriList.SetSigTrueAction(2923, () => Press(C(ShiftMode))); + TriList.SetSigTrueAction(2924, () => Press(D(ShiftMode))); + TriList.SetSigTrueAction(2925, () => Press(E(ShiftMode))); + TriList.SetSigTrueAction(2926, () => Press(F(ShiftMode))); + TriList.SetSigTrueAction(2927, () => Press(G(ShiftMode))); + TriList.SetSigTrueAction(2928, () => Press(H(ShiftMode))); + TriList.SetSigTrueAction(2929, () => Press(I(ShiftMode))); + TriList.SetSigTrueAction(2930, () => Press(J(ShiftMode))); + TriList.SetSigTrueAction(2931, () => Press(K(ShiftMode))); + TriList.SetSigTrueAction(2932, () => Press(L(ShiftMode))); + TriList.SetSigTrueAction(2933, () => Press(M(ShiftMode))); + TriList.SetSigTrueAction(2934, () => Press(N(ShiftMode))); + TriList.SetSigTrueAction(2935, () => Press(O(ShiftMode))); + TriList.SetSigTrueAction(2936, () => Press(P(ShiftMode))); + TriList.SetSigTrueAction(2937, () => Press(Q(ShiftMode))); + TriList.SetSigTrueAction(2938, () => Press(R(ShiftMode))); + TriList.SetSigTrueAction(2939, () => Press(S(ShiftMode))); + TriList.SetSigTrueAction(2940, () => Press(T(ShiftMode))); + TriList.SetSigTrueAction(2941, () => Press(U(ShiftMode))); + TriList.SetSigTrueAction(2942, () => Press(V(ShiftMode))); + TriList.SetSigTrueAction(2943, () => Press(W(ShiftMode))); + TriList.SetSigTrueAction(2944, () => Press(X(ShiftMode))); + TriList.SetSigTrueAction(2945, () => Press(Y(ShiftMode))); + TriList.SetSigTrueAction(2946, () => Press(Z(ShiftMode))); + TriList.SetSigTrueAction(2947, () => Press('.')); + TriList.SetSigTrueAction(2948, () => Press('@')); + TriList.SetSigTrueAction(2949, () => Press(' ')); + TriList.SetSigHeldAction(2950, 500, StartBackspaceRepeat, StopBackspaceRepeat, Backspace); + //TriList.SetSigTrueAction(2950, Backspace); + TriList.SetSigTrueAction(2951, Shift); + TriList.SetSigTrueAction(2952, NumShift); + TriList.SetSigTrueAction(2953, Clear); + TriList.SetSigTrueAction(2954, () => Press(DotComButtonString)); + + TriList.SetBool(GoButtonVisibleJoin, GoButtonVisible); + TriList.SetString(GoButtonTextJoin, GoButtonText); + TriList.SetBool(SecondaryButtonVisibleJoin, SecondaryButtonVisible); + TriList.SetString(SecondaryButtonTextJoin, SecondaryButtonText); + + TriList.SetBool(KeyboardVisible, true); + ShowKeys(); + IsVisible = true; + } + + /// + /// Hides the keyboard and disconnects ALL sig handlers from 2901 - 2969 + /// + public void Hide() + { + if (!IsVisible) + return; + + for (uint i = 2901; i < 2970; i++) + TriList.ClearBoolSigAction(i); + + // run attached actions + if(HideAction != null) + HideAction(); + + TriList.SetBool(KeyboardVisible, false); + IsVisible = false; + } + + /// + /// + /// + /// + public void Press(char c) + { + OnKeyPress(c.ToString()); + Output.Append(c); + OutputFeedback.FireUpdate(); + ResetShift(); + } + + /// + /// + /// + /// + public void Press(string s) + { + OnKeyPress(s); + Output.Append(s); + OutputFeedback.FireUpdate(); + ResetShift(); + } + + /// + /// + /// + public void EnableGoButton() + { + TriList.SetBool(GoButtonEnableJoin, true); + } + + /// + /// + /// + public void DisableGoButton() + { + TriList.SetBool(GoButtonEnableJoin, false); + } + + void ResetShift() + { + if (ShiftMode == 1) + { + ShiftMode = 0; + ShowKeys(); + } + else if (ShiftMode == 3) + { + ShiftMode = 2; + ShowKeys(); + } + } + + char A(int i) { return new char[] { 'a', 'A', '?', '?' }[i]; } + char B(int i) { return new char[] { 'b', 'B', ':', ':' }[i]; } + char C(int i) { return new char[] { 'c', 'C', '>', '>' }[i]; } + char D(int i) { return new char[] { 'd', 'D', '_', '_' }[i]; } + char E(int i) { return new char[] { 'e', 'E', '3', '#' }[i]; } + char F(int i) { return new char[] { 'f', 'F', '=', '=' }[i]; } + char G(int i) { return new char[] { 'g', 'G', '+', '+' }[i]; } + char H(int i) { return new char[] { 'h', 'H', '[', '[' }[i]; } + char I(int i) { return new char[] { 'i', 'I', '8', '*' }[i]; } + char J(int i) { return new char[] { 'j', 'J', ']', ']' }[i]; } + char K(int i) { return new char[] { 'k', 'K', '/', '/' }[i]; } + char L(int i) { return new char[] { 'l', 'L', '\\', '\\' }[i]; } + char M(int i) { return new char[] { 'm', 'M', '"', '"' }[i]; } + char N(int i) { return new char[] { 'n', 'N', '\'', '\'' }[i]; } + char O(int i) { return new char[] { 'o', 'O', '9', '(' }[i]; } + char P(int i) { return new char[] { 'p', 'P', '0', ')' }[i]; } + char Q(int i) { return new char[] { 'q', 'Q', '1', '!' }[i]; } + char R(int i) { return new char[] { 'r', 'R', '4', '$' }[i]; } + char S(int i) { return new char[] { 's', 'S', '-', '-' }[i]; } + char T(int i) { return new char[] { 't', 'T', '5', '%' }[i]; } + char U(int i) { return new char[] { 'u', 'U', '7', '&' }[i]; } + char V(int i) { return new char[] { 'v', 'V', ';', ';' }[i]; } + char W(int i) { return new char[] { 'w', 'W', '2', '@' }[i]; } + char X(int i) { return new char[] { 'x', 'X', '<', '<' }[i]; } + char Y(int i) { return new char[] { 'y', 'Y', '6', '^' }[i]; } + char Z(int i) { return new char[] { 'z', 'Z', ',', ',' }[i]; } + + /// + /// Does what it says + /// + void StartBackspaceRepeat() + { + if (BackspaceTimer == null) + { + BackspaceTimer = new CTimer(o => Backspace(), null, 0, 175); + } + } + + /// + /// Does what it says + /// + void StopBackspaceRepeat() + { + if (BackspaceTimer != null) + { + BackspaceTimer.Stop(); + BackspaceTimer = null; + } + } + + void Backspace() + { + OnKeyPress(KeyboardSpecialKey.Backspace); + + if (Output.Length > 0) + { + Output.Remove(Output.Length - 1, 1); + OutputFeedback.FireUpdate(); + } + } + + void Clear() + { + OnKeyPress(KeyboardSpecialKey.Clear); + + Output.Remove(0, Output.Length); + OutputFeedback.FireUpdate(); + } + + /* When in mode 0 (lowercase): + * shift button: up arrow 0 + * numShift button: 123/#$@#$ 0 + * + * - shift --> mode 1 + * - double-tap shift --> caps lock + * - numShift --> mode 2 + * + * mode 1 (uppercase) + * shift button: down arrow 1 + * numShift button: 123/##$# 0 + * + * - shift --> mode 0 + * - numShift --> mode 2 + * + * - Tapping any key will go back to mode 0 + * + * mode 2 (numbers-sym) + * Shift button: #$#$#$ 2 + * numShift: ABC 1 + * + * - shift --> mode 3 + * - double-tap shift --> caps lock + * - numShift --> mode 0 + * + * mode 3 (sym) + * Shift button: 123 3 + * numShift: ABC 1 + * + * - shift --> mode 2 + * - numShift --> mode 0 + * + * - Tapping any key will go back to mode 2 + */ + void Shift() + { + if (ShiftMode == 0) + ShiftMode = 1; + else if (ShiftMode == 1) + ShiftMode = 0; + else if (ShiftMode == 2) + ShiftMode = 3; + else + ShiftMode = 2; + + ShowKeys(); + } + + void NumShift() + { + if (ShiftMode == 0 || ShiftMode == 1) + ShiftMode = 2; + else if (ShiftMode == 2 || ShiftMode == 3) + ShiftMode = 0; + ShowKeys(); + } + + void ShowKeys() + { + TriList.SetString(2921, A(ShiftMode).ToString()); + TriList.SetString(2922, B(ShiftMode).ToString()); + TriList.SetString(2923, C(ShiftMode).ToString()); + TriList.SetString(2924, D(ShiftMode).ToString()); + TriList.SetString(2925, E(ShiftMode).ToString()); + TriList.SetString(2926, F(ShiftMode).ToString()); + TriList.SetString(2927, G(ShiftMode).ToString()); + TriList.SetString(2928, H(ShiftMode).ToString()); + TriList.SetString(2929, I(ShiftMode).ToString()); + TriList.SetString(2930, J(ShiftMode).ToString()); + TriList.SetString(2931, K(ShiftMode).ToString()); + TriList.SetString(2932, L(ShiftMode).ToString()); + TriList.SetString(2933, M(ShiftMode).ToString()); + TriList.SetString(2934, N(ShiftMode).ToString()); + TriList.SetString(2935, O(ShiftMode).ToString()); + TriList.SetString(2936, P(ShiftMode).ToString()); + TriList.SetString(2937, Q(ShiftMode).ToString()); + TriList.SetString(2938, R(ShiftMode).ToString()); + TriList.SetString(2939, S(ShiftMode).ToString()); + TriList.SetString(2940, T(ShiftMode).ToString()); + TriList.SetString(2941, U(ShiftMode).ToString()); + TriList.SetString(2942, V(ShiftMode).ToString()); + TriList.SetString(2943, W(ShiftMode).ToString()); + TriList.SetString(2944, X(ShiftMode).ToString()); + TriList.SetString(2945, Y(ShiftMode).ToString()); + TriList.SetString(2946, Z(ShiftMode).ToString()); + TriList.SetString(2954, DotComButtonString); + + TriList.SetUshort(2951, (ushort)ShiftMode); // 0 = up, 1 = down, 2 = #, 3 = 123 + TriList.SetUshort(2952, (ushort)(ShiftMode < 2 ? 0 : 1)); // 0 = #, 1 = abc + } + + /// + /// Event fire helper for text + /// + /// + void OnKeyPress(string text) + { + var handler = KeyPress; + if (handler != null) + KeyPress(this, new KeyboardControllerPressEventArgs(text)); + } + + /// + /// event helper for special keys + /// + /// + void OnKeyPress(KeyboardSpecialKey key) + { + var handler = KeyPress; + if (handler != null) + KeyPress(this, new KeyboardControllerPressEventArgs(key)); + } + + + /// + /// 2901 + /// + public const uint KeyboardVisible = 2901; + /// + /// 2902 + /// + public const uint ClosePressJoin = 2902; + /// + /// 2903 + /// + public const uint GoButtonPressJoin = 2903; + /// + /// 2903 + /// + public const uint GoButtonTextJoin = 2903; + /// + /// 2904 + /// + public const uint SecondaryButtonPressJoin = 2904; + /// + /// 2904 + /// + public const uint SecondaryButtonTextJoin = 2904; + /// + /// 2905 + /// + public const uint GoButtonVisibleJoin = 2905; + /// + /// 2906 + /// + public const uint SecondaryButtonVisibleJoin = 2906; + /// + /// 2907 + /// + public const uint GoButtonEnableJoin = 2907; + /// + /// 2910 + /// + public const uint ClearPressJoin = 2910; + /// + /// 2911 + /// + public const uint ClearVisibleJoin = 2911; + + } + + /// + /// + /// + public class KeyboardControllerPressEventArgs : EventArgs + { + public string Text { get; private set; } + public KeyboardSpecialKey SpecialKey { get; private set; } + + public KeyboardControllerPressEventArgs(string text) + { + Text = text; + } + + public KeyboardControllerPressEventArgs(KeyboardSpecialKey key) + { + SpecialKey = key; + } + } + + public enum KeyboardSpecialKey + { + None = 0, Backspace, Clear, GoButton, SecondaryButton + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/MOVED LargeTouchpanelControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/MOVED LargeTouchpanelControllerBase.cs new file mode 100644 index 00000000..97c937b3 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/MOVED LargeTouchpanelControllerBase.cs @@ -0,0 +1,275 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using Crestron.SimplSharp; +//using Crestron.SimplSharpPro; +//using Crestron.SimplSharpPro.DeviceSupport; + +//using PepperDash.Core; + + +//namespace PepperDash.Essentials.Core +//{ +// /// +// /// +// /// +// public class LargeTouchpanelControllerBase : SmartGraphicsTouchpanelControllerBase +// { +// public string PresentationShareButtonInVideoText = "Share"; +// public string PresentationShareButtonNotInVideoText = "Presentation"; + +// SourceListSubpageReferenceList SourceSelectSRL; +// DevicePageControllerBase CurrentPresentationSourcePageController; + +// public LargeTouchpanelControllerBase(string key, string name, +// BasicTriListWithSmartObject triList, string sgdFilePath) +// : base(key, name, triList, sgdFilePath) +// { +// } + +// public override bool CustomActivate() +// { +// var baseSuccess = base.CustomActivate(); +// if (!baseSuccess) return false; + +// SourceSelectSRL = new SourceListSubpageReferenceList(this.TriList, n => +// { if (CurrentRoom != null) CurrentRoom.SelectSource(n); }); + +// var lm = Global.LicenseManager; +// if (lm != null) +// { +// lm.LicenseIsValid.LinkInputSig(TriList.BooleanInput[UiCue.ShowLicensed.Number]); +// //others +// } + +// // Temp things ----------------------------------------------------------------------- +// TriList.StringInput[UiCue.SplashMessage.Number].StringValue = SplashMessage; +// //------------------------------------------------------------------------------------ + +// // Initialize initial view +// ShowSplashOrMain(); +// return true; +// } + +// /// +// /// In Essentials, this should NEVER be called, since it's a one-room solution +// /// +// protected override void HideRoomUI() +// { +// // UI Cleanup here???? + +// //SwapAudioDeviceControls(CurrentRoom.CurrentAudioDevice, null); +// //CurrentRoom.AudioDeviceWillChange -= CurrentRoom_AudioDeviceWillChange; + +// CurrentRoom.IsCoolingDown.OutputChange -= CurrentRoom_IsCoolingDown_OutputChange; +// CurrentRoom.IsWarmingUp.OutputChange -= CurrentRoom_IsWarmingUp_OutputChange; + +// SourceSelectSRL.DetachFromCurrentRoom(); +// } + +// /// +// /// Ties this panel controller to the Room and gets updates. +// /// +// protected override void ShowRoomUI() +// { +// Debug.Console(1, this, "connecting to system '{0}'", CurrentRoom.Key); + +// TriList.StringInput[RoomCue.Name.Number].StringValue = CurrentRoom.Name; +// TriList.StringInput[RoomCue.Description.Number].StringValue = CurrentRoom.Description; + +// CurrentRoom.IsCoolingDown.OutputChange -= CurrentRoom_IsCoolingDown_OutputChange; +// CurrentRoom.IsWarmingUp.OutputChange -= CurrentRoom_IsWarmingUp_OutputChange; +// CurrentRoom.IsCoolingDown.OutputChange += CurrentRoom_IsCoolingDown_OutputChange; +// CurrentRoom.IsWarmingUp.OutputChange += CurrentRoom_IsWarmingUp_OutputChange; + +// SourceSelectSRL.AttachToRoom(CurrentRoom); +// } + +// void CurrentRoom_IsCoolingDown_OutputChange(object sender, EventArgs e) +// { +// Debug.Console(2, this, "Received room in cooldown={0}", CurrentRoom.IsCoolingDown.BoolValue); +// if (CurrentRoom.IsCoolingDown.BoolValue) // When entering cooldown +// { +// // Do we need to check for an already-running cooldown - like in the case of room switches? +// new ModalDialog(TriList).PresentModalTimerDialog(0, "Power Off", "Power", "Please wait, shutting down", +// "", "", CurrentRoom.CooldownTime, true, b => +// { +// ShowSplashOrMain(); +// }); +// } +// } + +// void CurrentRoom_IsWarmingUp_OutputChange(object sender, EventArgs e) +// { +// Debug.Console(2, this, "Received room in warmup={0}", CurrentRoom.IsWarmingUp.BoolValue); +// if (CurrentRoom.IsWarmingUp.BoolValue) // When entering warmup +// { +// // Do we need to check for an already-running cooldown - like in the case of room switches? +// new ModalDialog(TriList).PresentModalTimerDialog(0, "Power On", "Power", "Please wait, powering on", +// "", "", CurrentRoom.WarmupTime, false, b => +// { +// // Reveal sources - or has already been done behind modal +// }); +// } +// } + +// // Handler for source change events. +// void CurrentRoom_PresentationSourceChange(object sender, EssentialsRoomSourceChangeEventArgs args) +// { +// // Put away the old source and set up the new source. +// Debug.Console(2, this, "Received source change={0}", args.NewSource != null ? args.NewSource.Key : "none"); + +// // If we're in tech, don't switch screen modes. Add any other modes we may want to switch away from +// // inside the if below. +// if (MainMode == eMainModeType.Splash) +// setMainMode(eMainModeType.Presentation); +// SetControlSource(args.NewSource); +// } + +// //*********************************************************************** +// //** UI Manipulation +// //*********************************************************************** + +// /// +// /// Shows the splash page or the main presentation page, depending on config setting +// /// +// void ShowSplashOrMain() +// { +// if (UsesSplashPage) +// setMainMode(eMainModeType.Splash); +// else +// setMainMode(eMainModeType.Presentation); +// } + +// /// +// /// Switches between main modes +// /// +// void setMainMode(eMainModeType mode) +// { +// MainMode = mode; +// switch (mode) +// { +// case eMainModeType.Presentation: +// TriList.BooleanInput[UiCue.VisibleCommonFooter.Number].BoolValue = true; +// TriList.BooleanInput[UiCue.VisibleCommonHeader.Number].BoolValue = true; +// TriList.BooleanInput[UiCue.VisibleSplash.Number].BoolValue = false; +// TriList.BooleanInput[UiCue.VisiblePresentationSourceList.Number].BoolValue = true; +// ShowCurrentPresentationSourceUi(); +// break; +// case eMainModeType.Splash: +// TriList.BooleanInput[UiCue.VisibleCommonFooter.Number].BoolValue = false; +// TriList.BooleanInput[UiCue.VisibleCommonHeader.Number].BoolValue = false; +// TriList.BooleanInput[UiCue.VisiblePresentationSourceList.Number].BoolValue = false; +// TriList.BooleanInput[UiCue.VisibleSplash.Number].BoolValue = true; +// HideCurrentPresentationSourceUi(); +// break; +// case eMainModeType.Tech: +// new ModalDialog(TriList).PresentModalTimerDialog(1, "Tech page", "Info", +// "Tech page will be here soon!
I promise", +// "Bueno!", "", 0, false, null); +// MainMode = eMainModeType.Presentation; +// break; +// default: +// break; +// } +// } + +// void PowerOffWithConfirmPressed() +// { +// if (!CurrentRoom.RoomIsOn.BoolValue) return; +// // Timeout or button 1 press will shut down +// var modal = new ModalDialog(TriList); +// uint seconds = CurrentRoom.UnattendedShutdownTimeMs / 1000; +// var message = string.Format("Meeting will end in {0} seconds", seconds); +// modal.PresentModalTimerDialog(2, "End Meeting", "Info", message, +// "End Meeting Now", "Cancel", CurrentRoom.UnattendedShutdownTimeMs, true, +// but => { if (but != 2) CurrentRoom.RoomOff(); }); +// } + +// /// +// /// Reveals the basic UI for the current device +// /// +// protected override void ShowCurrentPresentationSourceUi() +// { +// if (MainMode == eMainModeType.Splash && CurrentRoom.RoomIsOn.BoolValue) +// setMainMode(eMainModeType.Presentation); + +// if (CurrentPresentationControlDevice == null) +// { +// // If system is off, do one thing + +// // Otherwise, do something else - shouldn't be in this condition + +// return; +// } + +// // If a controller is already loaded, use it +// if (LoadedPageControllers.ContainsKey(CurrentPresentationControlDevice)) +// CurrentPresentationSourcePageController = LoadedPageControllers[CurrentPresentationControlDevice]; +// else +// { +// // This is by no means optimal, but for now.... +// if (CurrentPresentationControlDevice.Type == PresentationSourceType.SetTopBox +// && CurrentPresentationControlDevice is IHasSetTopBoxProperties) +// CurrentPresentationSourcePageController = new PageControllerLargeSetTopBoxGeneric(TriList, +// CurrentPresentationControlDevice as IHasSetTopBoxProperties); + +// else if (CurrentPresentationControlDevice.Type == PresentationSourceType.Laptop) +// CurrentPresentationSourcePageController = new PageControllerLaptop(TriList); + +// // separate these... +// else if (CurrentPresentationControlDevice.Type == PresentationSourceType.Dvd) +// CurrentPresentationSourcePageController = +// new PageControllerLargeDvd(TriList, CurrentPresentationControlDevice as IHasCueActionList); + +// else +// CurrentPresentationSourcePageController = null; + +// // Save it. +// if (CurrentPresentationSourcePageController != null) +// LoadedPageControllers[CurrentPresentationControlDevice] = CurrentPresentationSourcePageController; +// } + +// if (CurrentPresentationSourcePageController != null) +// CurrentPresentationSourcePageController.SetVisible(true); +// } + +// protected override void HideCurrentPresentationSourceUi() +// { +// if (CurrentPresentationControlDevice != null && CurrentPresentationSourcePageController != null) +// CurrentPresentationSourcePageController.SetVisible(false); +// } + + + +// void ShowHelp() +// { +// new ModalDialog(TriList).PresentModalTimerDialog(1, "Help", "Help", CurrentRoom.HelpMessage, +// "OK", "", 0, false, null); +// } + +// protected void ListSmartObjects() +// { +// Debug.Console(0, this, "Smart objects IDs:"); +// var list = TriList.SmartObjects.OrderBy(s => s.Key); +// foreach (var kvp in list) +// Debug.Console(0, " {0}", kvp.Key); +// } + +// public override List FunctionList +// { +// get +// { +// return new List +// { +// new BoolCueActionPair(UiCue.PressSplash, b => { if(!b) setMainMode(eMainModeType.Presentation); }), +// new BoolCueActionPair(UiCue.PressRoomOffWithConfirm, b => { if(!b) PowerOffWithConfirmPressed(); }), +// new BoolCueActionPair(UiCue.PressModePresentationShare, b => { if(!b) setMainMode(eMainModeType.Presentation); }), +// new BoolCueActionPair(UiCue.PressHelp, b => { if(!b) ShowHelp(); }), +// new BoolCueActionPair(UiCue.PressSettings, b => { if(!b) setMainMode(eMainModeType.Tech); }), +// }; +// } +// } +// //#endregion +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/MOVED UIControllers/DevicePageControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/MOVED UIControllers/DevicePageControllerBase.cs new file mode 100644 index 00000000..334050e0 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/MOVED UIControllers/DevicePageControllerBase.cs @@ -0,0 +1,244 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; +//using Crestron.SimplSharpPro; +//using Crestron.SimplSharpPro.DeviceSupport; + +//using PepperDash.Essentials.Core.Presets; + +//using PepperDash.Core; + + +//namespace PepperDash.Essentials.Core +//{ +// /// +// /// +// /// +// public abstract class DevicePageControllerBase +// { + +// protected BasicTriListWithSmartObject TriList; +// protected List FixedObjectSigs; + +// public DevicePageControllerBase(BasicTriListWithSmartObject triList) +// { +// TriList = triList; +// } + +// public void SetVisible(bool state) +// { +// foreach (var sig in FixedObjectSigs) +// { +// Debug.Console(2, "set visible {0}={1}", sig.Number, state); +// sig.BoolValue = state; +// } +// CustomSetVisible(state); +// } + +// /// +// /// Add any specialized show/hide logic here - beyond FixedObjectSigs. Overriding +// /// methods do not need to call this base method +// /// +// protected virtual void CustomSetVisible(bool state) +// { +// } +// } + +// //public class InterlockedDevicePageController +// //{ +// // public List ObjectBoolJoins { get; set; } +// // public uint DefaultJoin { get; set; } +// //} + + + + +// ///// +// ///// +// ///// +// //public interface IHasSetTopBoxProperties +// //{ +// // bool HasDpad { get; } +// // bool HasPreset { get; } +// // bool HasDvr { get; } +// // bool HasNumbers { get; } +// // DevicePresetsModel PresetsModel { get; } +// // void LoadPresets(string filePath); +// //} + +// public class PageControllerLaptop : DevicePageControllerBase +// { +// public PageControllerLaptop(BasicTriListWithSmartObject tl) +// : base(tl) +// { +// FixedObjectSigs = new List +// { +// tl.BooleanInput[10092], // well +// tl.BooleanInput[11001] // Laptop info +// }; +// } +// } + +// ///// +// ///// +// ///// +// //public class PageControllerLargeDvd : DevicePageControllerBase +// //{ +// // IHasCueActionList Device; + +// // public PageControllerLargeDvd(BasicTriListWithSmartObject tl, IHasCueActionList device) +// // : base(tl) +// // { + +// // Device = device; +// // FixedObjectSigs = new List +// // { +// // tl.BooleanInput[10093], // well +// // tl.BooleanInput[10411], // DVD Dpad +// // tl.BooleanInput[10412] // everything else +// // }; +// // } + +// // protected override void CustomSetVisible(bool state) +// // { +// // // Hook up smart objects if applicable +// // if (Device != null) +// // { +// // var uos = (Device as IHasCueActionList).CueActionList; +// // SmartObjectHelper.LinkDpadWithUserObjects(TriList, 10411, uos, state); +// // } +// // } +// //} + + +// /// +// /// +// /// +// //public class PageControllerLargeSetTopBoxGeneric : DevicePageControllerBase +// //{ +// // // To-DO: Add properties for component subpage names. DpadPos1, DpadPos2... +// // // Derived classes can then insert special subpages for variations on given +// // // device types. Like DirecTV vs Comcast + +// // public uint DpadSmartObjectId { get; set; } +// // public uint NumberPadSmartObjectId { get; set; } +// // public uint PresetsSmartObjectId { get; set; } +// // public uint Position5TabsId { get; set; } + +// // IHasSetTopBoxProperties Device; +// // DevicePresetsView PresetsView; + + +// // bool ShowPosition5Tabs; +// // uint CurrentVisiblePosition5Item = 1; +// // Dictionary Position5SubpageJoins = new Dictionary +// // { +// // { 1, 10053 }, +// // { 2, 10054 } +// // }; + +// // public PageControllerLargeSetTopBoxGeneric(BasicTriListWithSmartObject tl, IHasSetTopBoxProperties device) +// // : base(tl) +// // { +// // Device = device; +// // DpadSmartObjectId = 10011; +// // NumberPadSmartObjectId = 10014; +// // PresetsSmartObjectId = 10012; +// // Position5TabsId = 10081; + +// // bool dpad = device.HasDpad; +// // bool preset = device.HasPreset; +// // bool dvr = device.HasDvr; +// // bool numbers = device.HasNumbers; +// // uint[] joins = null; + +// // if (dpad && !preset && !dvr && !numbers) joins = new uint[] { 10031, 10091 }; +// // else if (!dpad && preset && !dvr && !numbers) joins = new uint[] { 10032, 10091 }; +// // else if (!dpad && !preset && dvr && !numbers) joins = new uint[] { 10033, 10091 }; +// // else if (!dpad && !preset && !dvr && numbers) joins = new uint[] { 10034, 10091 }; + +// // else if (dpad && preset && !dvr && !numbers) joins = new uint[] { 10042, 10021, 10092 }; +// // else if (dpad && !preset && dvr && !numbers) joins = new uint[] { 10043, 10021, 10092 }; +// // else if (dpad && !preset && !dvr && numbers) joins = new uint[] { 10044, 10021, 10092 }; +// // else if (!dpad && preset && dvr && !numbers) joins = new uint[] { 10043, 10022, 10092 }; +// // else if (!dpad && preset && !dvr && numbers) joins = new uint[] { 10044, 10022, 10092 }; +// // else if (!dpad && !preset && dvr && numbers) joins = new uint[] { 10044, 10023, 10092 }; + +// // else if (dpad && preset && dvr && !numbers) joins = new uint[] { 10053, 10032, 10011, 10093 }; +// // else if (dpad && preset && !dvr && numbers) joins = new uint[] { 10054, 10032, 10011, 10093 }; +// // else if (dpad && !preset && dvr && numbers) joins = new uint[] { 10054, 10033, 10011, 10093 }; +// // else if (!dpad && preset && dvr && numbers) joins = new uint[] { 10054, 10033, 10012, 10093 }; + +// // else if (dpad && preset && dvr && numbers) +// // { +// // joins = new uint[] { 10081, 10032, 10011, 10093 }; // special case +// // ShowPosition5Tabs = true; +// // } +// // // Project the joins into corresponding sigs. +// // FixedObjectSigs = joins.Select(u => TriList.BooleanInput[u]).ToList(); + +// // // Build presets +// // if (device.HasPreset) +// // { +// // PresetsView = new DevicePresetsView(tl, device.PresetsModel); +// // } + + +// // } + +// // protected override void CustomSetVisible(bool state) +// // { +// // if (ShowPosition5Tabs) +// // { +// // // Show selected tab +// // TriList.BooleanInput[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = state; + +// // var tabSo = TriList.SmartObjects[Position5TabsId]; +// // if (state) // Link up the tab object + +// // { +// // tabSo.BooleanOutput["Tab Button 1 Press"].UserObject = new BoolCueActionPair(b => ShowTab(1)); +// // tabSo.BooleanOutput["Tab Button 2 Press"].UserObject = new BoolCueActionPair(b => ShowTab(2)); +// // } +// // else // Disco tab object +// // { +// // tabSo.BooleanOutput["Tab Button 1 Press"].UserObject = null; +// // tabSo.BooleanOutput["Tab Button 2 Press"].UserObject = null; +// // } +// // } + +// // // Hook up smart objects if applicable +// // if (Device is IHasCueActionList) +// // { +// // var uos = (Device as IHasCueActionList).CueActionList; +// // SmartObjectHelper.LinkDpadWithUserObjects(TriList, DpadSmartObjectId, uos, state); +// // SmartObjectHelper.LinkNumpadWithUserObjects(TriList, NumberPadSmartObjectId, +// // uos, CommonBoolCue.Dash, CommonBoolCue.Last, state); +// // } + + +// // // Link, unlink presets +// // if (Device.HasPreset && state) +// // PresetsView.Attach(); +// // else if (Device.HasPreset && !state) +// // PresetsView.Detach(); +// // } + +// // void ShowTab(uint number) +// // { +// // // Ignore re-presses +// // if (CurrentVisiblePosition5Item == number) return; +// // // Swap subpage +// // var bi = TriList.BooleanInput; +// // if(CurrentVisiblePosition5Item > 0) +// // bi[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = false; +// // CurrentVisiblePosition5Item = number; +// // bi[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = true; + +// // // Show feedback on buttons +// // } + +// //} +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/ModalDialog.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/ModalDialog.cs new file mode 100644 index 00000000..3b2ff89b --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/ModalDialog.cs @@ -0,0 +1,211 @@ +using System; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public class ModalDialog + { + /// + /// Bool press 3991 + /// + public const uint Button1Join = 3991; + /// + /// Bool press 3992 + /// + public const uint Button2Join = 3992; + /// + /// 3993 + /// + public const uint CancelButtonJoin = 3993; + /// + ///For visibility of single button. Bool feedback 3994 + /// + public const uint OneButtonVisibleJoin = 3994; + /// + /// For visibility of two buttons. Bool feedback 3995. + /// + public const uint TwoButtonVisibleJoin = 3995; + /// + /// Shows the timer guage if in use. Bool feedback 3996 + /// + public const uint TimerVisibleJoin = 3996; + /// + /// Visibility join to show "X" button 3997 + /// + public const uint CancelVisibleJoin = 3997; + /// + /// Shows the modal subpage. Boolean feeback join 3999 + /// + public const uint ModalVisibleJoin = 3999; + + /// + /// The seconds value of the countdown timer. Ushort join 3991 + /// + //public const uint TimerSecondsJoin = 3991; + /// + /// The full ushort value of the countdown timer for a gauge. Ushort join 3992 + /// + public const uint TimerGaugeJoin = 3992; + + /// + /// Text on button one. String join 3991 + /// + public const uint Button1TextJoin = 3991; + /// + /// Text on button two. String join 3992 + /// + public const uint Button2TextJoin = 3992; + /// + /// Message text. String join 3994 + /// + public const uint MessageTextJoin = 3994; + /// + /// Title text. String join 3995 + /// + public const uint TitleTextJoin = 3995; + /// + /// Icon name. String join 3996 + /// + public const uint IconNameJoin = 3996; + + /// + /// Returns true when modal is showing + /// + public bool ModalIsVisible + { + get { return TriList.BooleanInput[ModalVisibleJoin].BoolValue; } + } + + /// + /// + /// + public bool CanCancel { get; private set; } + + + BasicTriList TriList; + + Action ModalCompleteAction; + + static object CompleteActionLock = new object(); + + /// + /// Creates a new modal to be shown on provided TriList + /// + /// + public ModalDialog(BasicTriList triList) + { + TriList = triList; + // Attach actions to buttons + + triList.SetSigFalseAction(Button1Join, () => OnModalComplete(1)); + triList.SetSigFalseAction(Button2Join, () => OnModalComplete(2)); + triList.SetSigFalseAction(CancelButtonJoin, () => { if (CanCancel) CancelDialog(); }); + CanCancel = true; + } + + /// + /// Shows the dialog + /// + /// Number of buttons to show. 0, 1, 2 + /// The amount of time to show the dialog. Use 0 for no timeout. + /// If the progress bar gauge needs to count down instead of up + /// The action to run when the dialog is dismissed. Parameter will be 1 or 2 if button pressed, or 0 if dialog times out + /// True when modal is created. + public bool PresentModalDialog(uint numberOfButtons, string title, string iconName, + string message, string button1Text, + string button2Text, bool showGauge, bool showCancel, Action completeAction) + { + // Don't reset dialog if visible now + if (!ModalIsVisible) + { + ModalCompleteAction = completeAction; + TriList.StringInput[TitleTextJoin].StringValue = title; + if (string.IsNullOrEmpty(iconName)) iconName = "Blank"; + TriList.StringInput[IconNameJoin].StringValue = iconName; + TriList.StringInput[MessageTextJoin].StringValue = message; + if (numberOfButtons == 0) + { + // Show no buttons + TriList.BooleanInput[OneButtonVisibleJoin].BoolValue = false; + TriList.BooleanInput[TwoButtonVisibleJoin].BoolValue = false; + } + else if (numberOfButtons == 1) + { + // Show one button + TriList.BooleanInput[OneButtonVisibleJoin].BoolValue = true; + TriList.BooleanInput[TwoButtonVisibleJoin].BoolValue = false; + TriList.StringInput[Button1TextJoin].StringValue = button1Text; + } + else if (numberOfButtons == 2) + { + // Show two + TriList.BooleanInput[OneButtonVisibleJoin].BoolValue = false; + TriList.BooleanInput[TwoButtonVisibleJoin].BoolValue = true; + TriList.StringInput[Button1TextJoin].StringValue = button1Text; + TriList.StringInput[Button2TextJoin].StringValue = button2Text; + } + // Show/hide guage + TriList.BooleanInput[TimerVisibleJoin].BoolValue = showGauge; + + CanCancel = showCancel; + TriList.BooleanInput[CancelVisibleJoin].BoolValue = showCancel; + + //Reveal and activate + TriList.BooleanInput[ModalVisibleJoin].BoolValue = true; + + WakePanel(); + + return true; + } + + return false; + } + + /// + /// Wakes the panel by turning on the backlight if off + /// + public void WakePanel() + { + try + { + var panel = TriList as TswFt5Button; + + if (panel != null && panel.ExtenderSystemReservedSigs.BacklightOffFeedback.BoolValue) + panel.ExtenderSystemReservedSigs.BacklightOn(); + } + catch + { + Debug.Console(1, "Error Waking Panel. Maybe testing with Xpanel?"); + } + } + + /// + /// Hide dialog from elsewhere, fires CompleteAction + /// + public void CancelDialog() + { + OnModalComplete(0); + } + + /// + /// Hides dialog. Fires no action + /// + public void HideDialog() + { + TriList.BooleanInput[ModalVisibleJoin].BoolValue = false; + } + + // When the modal is cleared or times out, clean up the various bits + void OnModalComplete(uint buttonNum) + { + TriList.BooleanInput[ModalVisibleJoin].BoolValue = false; + + var action = ModalCompleteAction; + if (action != null) + action(buttonNum); + } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/REMOVE Tsr302Controller.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/REMOVE Tsr302Controller.cs new file mode 100644 index 00000000..13cfabcc --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/REMOVE Tsr302Controller.cs @@ -0,0 +1,135 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using Crestron.SimplSharp; +//using Crestron.SimplSharpPro; +//using Crestron.SimplSharpPro.DeviceSupport; +//using Crestron.SimplSharpPro.UI; + +//using PepperDash.Core; + + +//namespace PepperDash.Essentials.Core +//{ +//[Obsolete("Replaced, initially with CrestronTsr302Controller in Resissentials")] +// public class Tsr302Controller : SmartGraphicsTouchpanelControllerBase +// { +// //public override List FunctionList +// //{ +// // get +// // { +// // return new List +// // { + +// // }; +// // } +// //} + +// public Tsr302 Remote { get; private set; } + +// SourceListSubpageReferenceList SourceSelectSRL; +// DevicePageControllerBase CurrentPresentationSourcePageController; +// CTimer VolumeFeedbackTimer; + + +// public Tsr302Controller(string key, string name, Tsr302 device, string sgdFilePath) : +// base(key, name, device, sgdFilePath) +// { +// // Base takes care of TriList +// Remote = device; +// Remote.Home.UserObject = new BoolCueActionPair(b => { if (!b) PressHome(); }); +// Remote.VolumeUp.UserObject = new BoolCueActionPair(b => { if (!b) PressHome(); }); +// Remote.ButtonStateChange += Remote_ButtonStateChange; +// } + +// public override bool CustomActivate() +// { +// var baseSuccess = base.CustomActivate(); +// if (!baseSuccess) return false; + +// SourceSelectSRL = new SourceListSubpageReferenceList(this.TriList, n => +// { if (CurrentRoom != null) CurrentRoom.SelectSource(n); }); + + +// return true; +// } + +// protected override void SwapAudioDeviceControls(IVolumeFunctions oldDev, IVolumeFunctions newDev) +// { +// // stop presses +// if (oldDev != null) +// { +// ReleaseAudioPresses(); +// if (oldDev is IVolumeTwoWay) +// { +// (newDev as IVolumeTwoWay).VolumeLevelFeedback.OutputChange -= VolumeLevelOutput_OutputChange; +// (oldDev as IVolumeTwoWay).VolumeLevelFeedback +// .UnlinkInputSig(TriList.UShortInput[CommonIntCue.MainVolumeLevel.Number]); +// } +// } + +// if (newDev != null) +// { +// Remote.VolumeDown.UserObject = newDev.VolumeDownCueActionPair; +// Remote.VolumeUp.UserObject = newDev.VolumeUpCueActionPair; +// Remote.Mute.UserObject = newDev.MuteToggleCueActionPair; +// if (newDev is IVolumeTwoWay) +// { +// var vOut = (newDev as IVolumeTwoWay).VolumeLevelFeedback; +// vOut.OutputChange += VolumeLevelOutput_OutputChange; +// TriList.UShortInput[CommonIntCue.MainVolumeLevel.Number].UShortValue = vOut.UShortValue; +// } +// } +// else +// { +// Remote.VolumeDown.UserObject = null; +// Remote.VolumeUp.UserObject = null; +// Remote.Mute.UserObject = null; +// } + +// base.SwapAudioDeviceControls(oldDev, newDev); +// } + +// void PressHome() +// { + +// } + +// void VolumeLevelOutput_OutputChange(object sender, EventArgs e) +// { +// // Set level and show popup on timer +// TriList.UShortInput[CommonIntCue.MainVolumeLevel.Number].UShortValue = +// (sender as IntFeedback).UShortValue; + +// if (VolumeFeedbackTimer == null) +// { +// TriList.BooleanInput[CommonBoolCue.ShowVolumeSlider.Number].BoolValue = true; +// VolumeFeedbackTimer = new CTimer(o => { +// TriList.BooleanInput[CommonBoolCue.ShowVolumeSlider.Number].BoolValue = false; +// }, 1000); +// } + +// } + +// void ReleaseAudioPresses() +// { +// if (Remote.VolumeDown.UserObject is BoolCueActionPair && Remote.VolumeDown.State == eButtonState.Pressed) +// (Remote.VolumeDown.UserObject as BoolCueActionPair).Invoke(false); +// if (Remote.VolumeUp.UserObject is BoolCueActionPair && Remote.VolumeUp.State == eButtonState.Pressed) +// (Remote.VolumeUp.UserObject as BoolCueActionPair).Invoke(false); +// if (Remote.Mute.UserObject is BoolCueActionPair && Remote.Mute.State == eButtonState.Pressed) +// (Remote.Mute.UserObject as BoolCueActionPair).Invoke(false); +// } + +// /// +// /// Handler. Run UO's stored in buttons +// /// +// void Remote_ButtonStateChange(GenericBase device, ButtonEventArgs args) +// { +// Debug.Console(2, this, "{0}={1}", args.Button.Name, args.Button.State); +// var uo = args.Button.UserObject as BoolCueActionPair; +// if (uo != null) +// uo.Invoke(args.NewButtonState == eButtonState.Pressed); +// } +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/SmartGraphicsTouchpanelControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/SmartGraphicsTouchpanelControllerBase.cs new file mode 100644 index 00000000..42c365d1 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/SmartGraphicsTouchpanelControllerBase.cs @@ -0,0 +1,309 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using Crestron.SimplSharp; +//using Crestron.SimplSharpPro; +//using Crestron.SimplSharpPro.DeviceSupport; + + +//namespace PepperDash.Essentials.Core +//{ +// public abstract class SmartGraphicsTouchpanelControllerBase : CrestronGenericBaseDevice +// { +// public BasicTriListWithSmartObject TriList { get; protected set; } +// public bool UsesSplashPage { get; set; } +// public string SplashMessage { get; set; } +// public bool ShowDate +// { +// set { TriList.BooleanInput[UiCue.ShowDate.Number].BoolValue = value; } +// } +// public bool ShowTime +// { +// set { TriList.BooleanInput[UiCue.ShowTime.Number].BoolValue = value; } +// } + +// public abstract List FunctionList { get; } + + +// protected eMainModeType MainMode; +// protected IPresentationSource CurrentPresentationControlDevice; + +// /// +// /// Defines the signal offset for the presentation device. Defaults to 100 +// /// +// public uint PresentationControlDeviceJoinOffset { get { return 100; } } + +// public enum eMainModeType +// { +// Presentation, Splash, Tech +// } + +// protected string SgdFilePath; +// public EssentialsRoom CurrentRoom { get; protected set; } +// protected Dictionary LoadedPageControllers +// = new Dictionary(); + +// static object RoomChangeLock = new object(); + +// /// +// /// Constructor +// /// +// public SmartGraphicsTouchpanelControllerBase(string key, string name, BasicTriListWithSmartObject triList, +// string sgdFilePath) +// : base(key, name, triList) +// { +// TriList = triList; +// if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key"); +// if (string.IsNullOrEmpty(sgdFilePath)) throw new ArgumentNullException("sgdFilePath"); +// SgdFilePath = sgdFilePath; +// TriList.LoadSmartObjects(SgdFilePath); +// UsesSplashPage = true; +// SplashMessage = "Welcome"; +// TriList.SigChange += Tsw_AnySigChange; +// foreach (var kvp in TriList.SmartObjects) +// kvp.Value.SigChange += this.Tsw_AnySigChange; +// } + +// public override bool CustomActivate() +// { +// var baseSuccess = base.CustomActivate(); +// if (!baseSuccess) return false; + +// // Wiring up the buttons with UOs +// foreach (var uo in this.FunctionList) +// { +// if (uo.Cue.Number == 0) continue; +// if (uo is BoolCueActionPair) +// TriList.BooleanOutput[uo.Cue.Number].UserObject = uo; +// else if (uo is UShortCueActionPair) +// TriList.UShortOutput[uo.Cue.Number].UserObject = uo; +// else if (uo is StringCueActionPair) +// TriList.StringOutput[uo.Cue.Number].UserObject = uo; +// } + +// return true; +// } + +// //public void SetCurrentRoom(EssentialsRoom room) +// //{ +// // if (CurrentRoom != null) +// // HideRoomUI(); +// // CurrentRoom = room; +// // ShowRoomUI(); +// //} + +// /// +// /// +// /// +// /// +// public void SetCurrentRoom(EssentialsRoom room) +// { +// if (CurrentRoom == room) return; + +// IVolumeFunctions oldAudio = null; +// //Disconnect current room and audio device +// if (CurrentRoom != null) +// { +// HideRoomUI(); +// CurrentRoom.AudioDeviceWillChange -= CurrentRoom_AudioDeviceWillChange; +// CurrentRoom.PresentationSourceChange -= CurrentRoom_PresentationSourceChange; +// oldAudio = CurrentRoom.CurrentAudioDevice; +// } + +// CurrentRoom = room; +// IVolumeFunctions newAudio = null; +// if (CurrentRoom != null) +// { +// CurrentRoom.AudioDeviceWillChange += this.CurrentRoom_AudioDeviceWillChange; +// CurrentRoom.PresentationSourceChange += this.CurrentRoom_PresentationSourceChange; +// SetControlSource(CurrentRoom.CurrentPresentationSource); +// newAudio = CurrentRoom.CurrentAudioDevice; +// ShowRoomUI(); +// } + +// SwapAudioDeviceControls(oldAudio, newAudio); +// } + +// /// +// /// Detaches and attaches an IVolumeFunctions device to the appropriate TP TriList signals. +// /// This will also add IVolumeNumeric if the device implements it. +// /// Overriding classes should call this. Overriding classes are responsible for +// /// linking up to hard keys, etc. +// /// +// /// May be null +// /// May be null +// protected virtual void SwapAudioDeviceControls(IVolumeFunctions oldDev, IVolumeFunctions newDev) +// { +// // Disconnect +// if (oldDev != null) +// { +// TriList.BooleanOutput[CommonBoolCue.VolumeDown.Number].UserObject = null; +// TriList.BooleanOutput[CommonBoolCue.VolumeUp.Number].UserObject = null; +// TriList.BooleanOutput[CommonBoolCue.MuteToggle.Number].UserObject = null; +// TriList.BooleanInput[CommonBoolCue.ShowVolumeButtons.Number].BoolValue = false; +// TriList.BooleanInput[CommonBoolCue.ShowVolumeSlider.Number].BoolValue = false; +// if (oldDev is IVolumeTwoWay) +// { +// TriList.UShortOutput[CommonIntCue.MainVolumeLevel.Number].UserObject = null; +// (oldDev as IVolumeTwoWay).IsMutedFeedback +// .UnlinkInputSig(TriList.BooleanInput[CommonBoolCue.MuteOn.Number]); +// (oldDev as IVolumeTwoWay).VolumeLevelFeedback +// .UnlinkInputSig(TriList.UShortInput[CommonIntCue.MainVolumeLevel.Number]); +// } +// } +// if (newDev != null) +// { +// TriList.BooleanInput[CommonBoolCue.ShowVolumeSlider.Number].BoolValue = true; +// TriList.BooleanOutput[CommonBoolCue.VolumeDown.Number].UserObject = newDev.VolumeDownCueActionPair; +// TriList.BooleanOutput[CommonBoolCue.VolumeUp.Number].UserObject = newDev.VolumeUpCueActionPair; +// TriList.BooleanOutput[CommonBoolCue.MuteToggle.Number].UserObject = newDev.MuteToggleCueActionPair; +// if (newDev is IVolumeTwoWay) +// { +// TriList.BooleanInput[CommonBoolCue.ShowVolumeSlider.Number].BoolValue = true; +// var numDev = newDev as IVolumeTwoWay; +// TriList.UShortOutput[CommonIntCue.MainVolumeLevel.Number].UserObject = numDev.VolumeLevelCueActionPair; +// numDev.VolumeLevelFeedback +// .LinkInputSig(TriList.UShortInput[CommonIntCue.MainVolumeLevel.Number]); +// } +// } +// } + + +// /// +// /// Does nothing. Override to add functionality when calling SetCurrentRoom +// /// +// protected virtual void HideRoomUI() { } + +// /// +// /// Does nothing. Override to add functionality when calling SetCurrentRoom +// /// +// protected virtual void ShowRoomUI() { } + +// /// +// /// Sets up the current presentation device and updates statuses if the device is capable. +// /// +// protected void SetControlSource(IPresentationSource newSource) +// { +// if (CurrentPresentationControlDevice != null) +// { +// HideCurrentPresentationSourceUi(); + +// // Unhook presses and things +// if (CurrentPresentationControlDevice is IHasCueActionList) +// { +// foreach (var uo in (CurrentPresentationControlDevice as IHasCueActionList).CueActionList) +// { +// if (uo.Cue.Number == 0) continue; +// if (uo is BoolCueActionPair) +// { +// var bSig = TriList.BooleanOutput[uo.Cue.Number]; +// // Disconnection should also clear bool sigs in case they are pressed and +// // might be orphaned +// if (bSig.BoolValue) +// (bSig.UserObject as BoolCueActionPair).Invoke(false); +// bSig.UserObject = null; +// } +// else if (uo is UShortCueActionPair) +// TriList.UShortOutput[uo.Cue.Number].UserObject = null; +// else if (uo is StringCueActionPair) +// TriList.StringOutput[uo.Cue.Number].UserObject = null; +// } +// } +// // unhook outputs +// if (CurrentPresentationControlDevice is IHasFeedback) +// { +// foreach (var output in (CurrentPresentationControlDevice as IHasFeedback).Feedbacks) +// { +// if (output.Cue.Number == 0) continue; +// if (output is BoolFeedback) +// (output as BoolFeedback).UnlinkInputSig(TriList.BooleanInput[output.Cue.Number]); +// else if (output is IntFeedback) +// (output as IntFeedback).UnlinkInputSig(TriList.UShortInput[output.Cue.Number]); +// else if (output is StringFeedback) +// (output as StringFeedback).UnlinkInputSig(TriList.StringInput[output.Cue.Number]); +// } +// } +// } +// CurrentPresentationControlDevice = newSource; +// //connect presses and things +// if (newSource is IHasCueActionList) // This has functions, get 'em +// { +// foreach (var ao in (newSource as IHasCueActionList).CueActionList) +// { +// if (ao.Cue.Number == 0) continue; +// if (ao is BoolCueActionPair) +// TriList.BooleanOutput[ao.Cue.Number].UserObject = ao; +// else if (ao is UShortCueActionPair) +// TriList.UShortOutput[ao.Cue.Number].UserObject = ao; +// else if (ao is StringCueActionPair) +// TriList.StringOutput[ao.Cue.Number].UserObject = ao; +// } +// } +// // connect outputs (addInputSig should update sig) +// if (CurrentPresentationControlDevice is IHasFeedback) +// { +// foreach (var output in (CurrentPresentationControlDevice as IHasFeedback).Feedbacks) +// { +// if (output.Cue.Number == 0) continue; +// if (output is BoolFeedback) +// (output as BoolFeedback).LinkInputSig(TriList.BooleanInput[output.Cue.Number]); +// else if (output is IntFeedback) +// (output as IntFeedback).LinkInputSig(TriList.UShortInput[output.Cue.Number]); +// else if (output is StringFeedback) +// (output as StringFeedback).LinkInputSig(TriList.StringInput[output.Cue.Number]); +// } +// } +// ShowCurrentPresentationSourceUi(); +// } + +// /// +// /// Reveals the basic UI for the current device +// /// +// protected virtual void ShowCurrentPresentationSourceUi() +// { +// } + +// /// +// /// Hides the UI for the current device and calls for a feedback signal cleanup +// /// +// protected virtual void HideCurrentPresentationSourceUi() +// { +// } + + +// /// +// /// +// /// +// void CurrentRoom_PresentationSourceChange(object sender, EssentialsRoomSourceChangeEventArgs args) +// { +// SetControlSource(args.NewSource); +// } + + +// /// +// /// +// /// +// void CurrentRoom_AudioDeviceWillChange(object sender, EssentialsRoomAudioDeviceChangeEventArgs e) +// { +// SwapAudioDeviceControls(e.OldDevice, e.NewDevice); +// } + + + +// /// +// /// Panel event handler +// /// +// void Tsw_AnySigChange(object currentDevice, SigEventArgs args) +// { +// // plugged in commands +// object uo = args.Sig.UserObject; + +// if (uo is Action) +// (uo as Action)(args.Sig.BoolValue); +// else if (uo is Action) +// (uo as Action)(args.Sig.UShortValue); +// else if (uo is Action) +// (uo as Action)(args.Sig.StringValue); +// } +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs new file mode 100644 index 00000000..b282d475 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Touchpanels/TriListExtensions.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + /// + /// Extensions used for more-clear attachment of Actions to user objects on sigs + /// + public static class SigAndTriListExtensions + { + /// + /// Attaches Action to Sig's user object and returns the same Sig. This provides no protection + /// from null sigs + /// + /// The BoolOutputSig to attach the Action to + /// An action to run when sig is pressed and when released + /// The Sig, sig + public static BoolOutputSig SetBoolSigAction(this BoolOutputSig sig, Action a) + { + sig.UserObject = a; + return sig; + } + + /// + /// Attaches Action to Sig's user object and returns the same Sig. + /// + /// + /// + /// + /// + public static BoolOutputSig SetBoolSigAction(this BasicTriList tl, uint sigNum, Action a) + { + return tl.BooleanOutput[sigNum].SetBoolSigAction(a); + } + + public static BoolOutputSig SetSigTrueAction(this BasicTriList tl, uint sigNum, Action a) + { + return tl.BooleanOutput[sigNum].SetBoolSigAction(b => { if(b) a(); }); + } + + /// + /// Attaches a void Action to a TriList's output sig's UserObject, to be run on release + /// + /// The sig + public static BoolOutputSig SetSigFalseAction(this BasicTriList tl, uint sigNum, Action a) + { + return tl.BooleanOutput[sigNum].SetBoolSigAction(b => { if (!b) a(); }); + } + + /// + /// Attaches a void Action to an output sig's UserObject, to be run on release + /// + /// The Sig + public static BoolOutputSig SetSigFalseAction(this BoolOutputSig sig, Action a) + { + return sig.SetBoolSigAction(b => { if (!b) a(); }); + } + + /// + /// Sets an action to a held sig + /// + /// The sig + public static BoolOutputSig SetSigHeldAction(this BasicTriList tl, uint sigNum, uint heldMs, Action heldAction) + { + return SetSigHeldAction(tl, sigNum, heldMs, heldAction, null); + } + + /// + /// Sets an action to a held sig as well as a released-without-hold action + /// + /// + public static BoolOutputSig SetSigHeldAction(this BoolOutputSig sig, uint heldMs, Action heldAction, Action holdReleasedAction, Action releaseAction) + { + CTimer heldTimer = null; + bool wasHeld = false; + return sig.SetBoolSigAction(press => + { + if (press) + { + wasHeld = false; + // Could insert a pressed action here + heldTimer = new CTimer(o => + { + // if still held and there's an action + if (sig.BoolValue && heldAction != null) + { + wasHeld = true; + // Hold action here + heldAction(); + } + }, heldMs); + } + else if (!press && !wasHeld) // released, no hold + { + heldTimer.Stop(); + if (releaseAction != null) + releaseAction(); + } + else // !press && wasHeld // released after held + { + heldTimer.Stop(); + if (holdReleasedAction != null) + holdReleasedAction(); + } + }); + + } + + /// + /// Sets an action to a held sig as well as a released-without-hold action + /// + /// The sig + public static BoolOutputSig SetSigHeldAction(this BasicTriList tl, uint sigNum, uint heldMs, Action heldAction, Action releaseAction) + { + return tl.BooleanOutput[sigNum].SetSigHeldAction(heldMs, heldAction, null, releaseAction); + } + + /// + /// Sets an action to a held sig, an action for the release of hold, as well as a released-without-hold action + /// + /// + public static BoolOutputSig SetSigHeldAction(this BasicTriList tl, uint sigNum, uint heldMs, Action heldAction, + Action holdReleasedAction, Action releaseAction) + { + return tl.BooleanOutput[sigNum].SetSigHeldAction(heldMs, heldAction, holdReleasedAction, releaseAction); + } + + /// + /// + /// + /// + /// + /// The Sig + public static UShortOutputSig SetUShortSigAction(this UShortOutputSig sig, Action a) + { + sig.UserObject = a; + return sig; + } + /// + /// + /// + /// + /// + /// + /// + public static UShortOutputSig SetUShortSigAction(this BasicTriList tl, uint sigNum, Action a) + { + return tl.UShortOutput[sigNum].SetUShortSigAction(a); + } + + /// + /// + /// + /// + /// + /// + public static StringOutputSig SetStringSigAction(this StringOutputSig sig, Action a) + { + sig.UserObject = a; + return sig; + } + + /// + /// + /// + /// + /// + /// + /// + public static StringOutputSig SetStringSigAction(this BasicTriList tl, uint sigNum, Action a) + { + return tl.StringOutput[sigNum].SetStringSigAction(a); + } + + /// + /// + /// + /// + /// + public static Sig ClearSigAction(this Sig sig) + { + sig.UserObject = null; + return sig; + } + + public static BoolOutputSig ClearBoolSigAction(this BasicTriList tl, uint sigNum) + { + return ClearSigAction(tl.BooleanOutput[sigNum]) as BoolOutputSig; + } + + public static UShortOutputSig ClearUShortSigAction(this BasicTriList tl, uint sigNum) + { + return ClearSigAction(tl.UShortOutput[sigNum]) as UShortOutputSig; + } + + public static StringOutputSig ClearStringSigAction(this BasicTriList tl, uint sigNum) + { + return ClearSigAction(tl.StringOutput[sigNum]) as StringOutputSig; + } + + /// + /// Helper method to set the value of a bool Sig on TriList + /// + public static void SetBool(this BasicTriList tl, uint sigNum, bool value) + { + tl.BooleanInput[sigNum].BoolValue = value; + } + + /// + /// Sends an true-false pulse to the sig + /// + /// + /// + public static void PulseBool(this BasicTriList tl, uint sigNum) + { + tl.BooleanInput[sigNum].Pulse(); + } + + /// + /// Sends a timed pulse to the sig + /// + /// + /// + /// + public static void PulseBool(this BasicTriList tl, uint sigNum, int ms) + { + tl.BooleanInput[sigNum].Pulse(ms); + } + + /// + /// Helper method to set the value of a ushort Sig on TriList + /// + public static void SetUshort(this BasicTriList tl, uint sigNum, ushort value) + { + tl.UShortInput[sigNum].UShortValue = value; + } + + /// + /// Helper method to set the value of a string Sig on TriList + /// + public static void SetString(this BasicTriList tl, uint sigNum, string value) + { + tl.StringInput[sigNum].StringValue = value; + } + + /// + /// Returns bool value of trilist sig + /// + /// + /// + /// + public static bool GetBool(this BasicTriList tl, uint sigNum) + { + return tl.BooleanOutput[sigNum].BoolValue; + } + + /// + /// Returns ushort value of trilist sig + /// + /// + /// + /// + public static ushort GetUshort(this BasicTriList tl, uint sigNum) + { + return tl.UShortOutput[sigNum].UShortValue; + } + + /// + /// Returns string value of trilist sig. + /// + /// + /// + /// + public static string GetString(this BasicTriList tl, uint sigNum) + { + return tl.StringOutput[sigNum].StringValue; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/TriListBridges/HandlerBridge.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/TriListBridges/HandlerBridge.cs new file mode 100644 index 00000000..e1a69707 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/TriListBridges/HandlerBridge.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + + +namespace PepperDash.Essentials.Core +{ + public abstract class HandlerBridge + { + public bool IsAttached { get; protected set; } + + /// + /// Attaches the handler to the panel's user objects + /// + public abstract void AttachToTriListOutputs(bool sendUpdate); + + /// + /// Removes the handler from the panel's user objects + /// + public abstract void DetachFromTriListOutputs(); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/BlurayPageManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/BlurayPageManager.cs new file mode 100644 index 00000000..c272a65a --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/BlurayPageManager.cs @@ -0,0 +1,42 @@ +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core.PageManagers +{ + public class DiscPlayerMediumPageManager : MediumLeftSwitchablePageManager + { + IDiscPlayerControls Player; + + public DiscPlayerMediumPageManager(IDiscPlayerControls player, BasicTriListWithSmartObject trilist) + : base(player.DisplayUiType) + { + Player = player; + TriList = trilist; + } + + public override void Show() + { + uint offset = GetOffsetJoin(); + BackingPageJoin = offset + 1; + AllLeftSubpages = new uint[] { 7, 8 }; + + if (LeftSubpageJoin == 0) + LeftSubpageJoin = offset + 8; // default to transport + TriList.BooleanInput[BackingPageJoin].BoolValue = true; + TriList.BooleanInput[LeftSubpageJoin].BoolValue = true; + + // Attach buttons to interlock + foreach(var p in AllLeftSubpages) + { + var p2 = p; // scope + TriList.SetSigFalseAction(10000 + p2, () => InterlockLeftSubpage(p2)); + } + } + + public override void Hide() + { + TriList.BooleanInput[BackingPageJoin].BoolValue = false; + TriList.BooleanInput[LeftSubpageJoin].BoolValue = false; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/PageManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/PageManager.cs new file mode 100644 index 00000000..1f261f87 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/PageManager.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core.PageManagers +{ + /// + /// The PageManager classes are used to bridge a device to subpage + /// visibility. + /// + public abstract class PageManager + { + protected List ActiveJoins = new List(); + + public abstract void Show(); + + public abstract void Hide(); + + /// + /// For device types 1-49, returns the offset join for subpage management 10100 - 14900 + /// + /// 1 through 49, as defined in some constants somewhere! + /// + public uint GetOffsetJoin(uint deviceType) + { + return 10000 + (deviceType * 100); + } + } + + /// + /// A simple class that hides and shows the default subpage for a given source type + /// + public class DefaultPageManager : PageManager + { + BasicTriList TriList; + uint BackingPageJoin; + + public DefaultPageManager(IUiDisplayInfo device, BasicTriList trilist) + { + TriList = trilist; + BackingPageJoin = GetOffsetJoin(device.DisplayUiType) + 1; + } + + public DefaultPageManager(uint join, BasicTriList trilist) + { + TriList = trilist; + BackingPageJoin = join; + } + + public override void Show() + { + TriList.BooleanInput[BackingPageJoin].BoolValue = true; + } + + public override void Hide() + { + TriList.BooleanInput[BackingPageJoin].BoolValue = false; + } + } + + /// + /// A page manager for a page with backing panel and a switchable side panel + /// + public abstract class MediumLeftSwitchablePageManager : PageManager + { + protected BasicTriListWithSmartObject TriList; + protected uint LeftSubpageJoin; + protected uint BackingPageJoin; + protected uint[] AllLeftSubpages; + protected uint DisplayUiType; + + protected MediumLeftSwitchablePageManager(uint displayUiType) + { + DisplayUiType = displayUiType; + } + + protected void InterlockLeftSubpage(uint join) + { + join = join + GetOffsetJoin(); + ClearLeftInterlock(); + TriList.BooleanInput[join].BoolValue = true; + LeftSubpageJoin = join; + } + + protected void ClearLeftInterlock() + { + foreach (var p in AllLeftSubpages) + TriList.BooleanInput[GetOffsetJoin() + p].BoolValue = false; + } + + protected uint GetOffsetJoin() + { + return GetOffsetJoin(DisplayUiType); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/SetTopBoxThreePanelPageManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/SetTopBoxThreePanelPageManager.cs new file mode 100644 index 00000000..f8838aed --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/SetTopBoxThreePanelPageManager.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Presets; + +namespace PepperDash.Essentials.Core.PageManagers +{ + public class ThreePanelPlusOnePageManager : PageManager + { + protected BasicTriListWithSmartObject TriList; + + public uint Position5TabsId { get; set; } + + /// + /// Show the tabs on the third panel + /// + protected bool ShowPosition5Tabs; + + /// + /// Joins that are always visible when this manager is visible + /// + protected uint[] FixedVisibilityJoins; + + /// + /// + /// + protected uint CurrentVisiblePosition5Item; + + /// + /// + /// + /// + public ThreePanelPlusOnePageManager(BasicTriListWithSmartObject trilist) + { + TriList = trilist; + CurrentVisiblePosition5Item = 1; + } + + /// + /// The joins for the switchable panel in position 5 + /// + Dictionary Position5SubpageJoins = new Dictionary + { + { 1, 10053 }, + { 2, 10054 } + }; + + /// + /// + /// + public override void Show() + { + // Project the joins into corresponding sigs. + var fixedSigs = FixedVisibilityJoins.Select(u => TriList.BooleanInput[u]).ToList(); + foreach (var sig in fixedSigs) + sig.BoolValue = true; + + if (ShowPosition5Tabs) + { + // Show selected tab + TriList.BooleanInput[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = true; + // hook up tab object + var tabSo = TriList.SmartObjects[Position5TabsId]; + tabSo.BooleanOutput["Tab Button 1 Press"].UserObject = new Action(b => { if (!b) ShowTab(1); }); + tabSo.BooleanOutput["Tab Button 2 Press"].UserObject = new Action(b => { if (!b) ShowTab(2); }); + tabSo.SigChange -= tabSo_SigChange; + tabSo.SigChange += tabSo_SigChange; + } + } + + void tabSo_SigChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.SmartObjectEventArgs args) + { + var uo = args.Sig.UserObject; + if(uo is Action) + (uo as Action)(args.Sig.BoolValue); + } + + public override void Hide() + { + var fixedSigs = FixedVisibilityJoins.Select(u => TriList.BooleanInput[u]).ToList(); + foreach (var sig in fixedSigs) + sig.BoolValue = false; + if (ShowPosition5Tabs) + { + TriList.BooleanInput[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = false; + + //var tabSo = TriList.SmartObjects[Position5TabsId]; + //tabSo.BooleanOutput["Tab Button 1 Press"].UserObject = null; + //tabSo.BooleanOutput["Tab Button 2 Press"].UserObject = null; + } + } + + void ShowTab(uint number) + { + // Ignore re-presses + if (CurrentVisiblePosition5Item == number) return; + // Swap subpage + var bi = TriList.BooleanInput; + if (CurrentVisiblePosition5Item > 0) + bi[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = false; + CurrentVisiblePosition5Item = number; + bi[Position5SubpageJoins[CurrentVisiblePosition5Item]].BoolValue = true; + } + } + + + + public class SetTopBoxThreePanelPageManager : ThreePanelPlusOnePageManager + { + ISetTopBoxControls SetTopBox; + DevicePresetsView PresetsView; + + public uint DpadSmartObjectId { get; set; } + public uint NumberPadSmartObjectId { get; set; } + public uint PresetsSmartObjectId { get; set; } + + /// + /// A page manager for set top box that shows some combination of four different panels, + /// in three slots on the page. + /// + /// + /// + public SetTopBoxThreePanelPageManager(ISetTopBoxControls stb, BasicTriListWithSmartObject trilist) + : base(trilist) + { + SetTopBox = stb; + TriList = trilist; + + DpadSmartObjectId = 10011; + NumberPadSmartObjectId = 10014; + PresetsSmartObjectId = 10012; + Position5TabsId = 10081; + + bool dpad = stb.HasDpad; + bool preset = stb.HasPresets; + bool dvr = stb.HasDvr; + bool numbers = stb.HasNumeric; + + if (dpad && !preset && !dvr && !numbers) FixedVisibilityJoins = new uint[] { 10031, 10091 }; + else if (!dpad && preset && !dvr && !numbers) FixedVisibilityJoins = new uint[] { 10032, 10091 }; + else if (!dpad && !preset && dvr && !numbers) FixedVisibilityJoins = new uint[] { 10033, 10091 }; + else if (!dpad && !preset && !dvr && numbers) FixedVisibilityJoins = new uint[] { 10034, 10091 }; + + else if (dpad && preset && !dvr && !numbers) FixedVisibilityJoins = new uint[] { 10042, 10021, 10092 }; + else if (dpad && !preset && dvr && !numbers) FixedVisibilityJoins = new uint[] { 10043, 10021, 10092 }; + else if (dpad && !preset && !dvr && numbers) FixedVisibilityJoins = new uint[] { 10044, 10021, 10092 }; + else if (!dpad && preset && dvr && !numbers) FixedVisibilityJoins = new uint[] { 10043, 10022, 10092 }; + else if (!dpad && preset && !dvr && numbers) FixedVisibilityJoins = new uint[] { 10044, 10022, 10092 }; + else if (!dpad && !preset && dvr && numbers) FixedVisibilityJoins = new uint[] { 10044, 10023, 10092 }; + + else if (dpad && preset && dvr && !numbers) FixedVisibilityJoins = new uint[] { 10053, 10032, 10011, 10093 }; + else if (dpad && preset && !dvr && numbers) FixedVisibilityJoins = new uint[] { 10054, 10032, 10011, 10093 }; + else if (dpad && !preset && dvr && numbers) FixedVisibilityJoins = new uint[] { 10054, 10033, 10011, 10093 }; + else if (!dpad && preset && dvr && numbers) FixedVisibilityJoins = new uint[] { 10054, 10033, 10012, 10093 }; + + else if (dpad && preset && dvr && numbers) + { + FixedVisibilityJoins = new uint[] { 10081, 10032, 10011, 10093 }; // special case + ShowPosition5Tabs = true; + } + // Bad config case + else + { + Debug.Console(1, stb, "WARNING: Not configured to show any UI elements"); + FixedVisibilityJoins = new uint[] { 10091 }; + } + + // Build presets + if (stb.HasPresets && stb.PresetsModel != null) + { + PresetsView = new DevicePresetsView(trilist, stb.PresetsModel); + } + } + + public override void Show() + { + if(PresetsView != null) + PresetsView.Attach(); + base.Show(); + } + + public override void Hide() + { + if (PresetsView != null) + PresetsView.Detach(); + base.Hide(); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/SetTopBoxTwoPanelPageManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/SetTopBoxTwoPanelPageManager.cs new file mode 100644 index 00000000..cb1e1840 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/SetTopBoxTwoPanelPageManager.cs @@ -0,0 +1,60 @@ +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Presets; + +namespace PepperDash.Essentials.Core.PageManagers +{ + /// + /// A fixed-layout page manager that expects a DPad on the right, fixed portion of the page, and a two/three + /// tab switchable area on the left for presets, numeric and transport controls + /// + public class SetTopBoxMediumPageManager : MediumLeftSwitchablePageManager + { + ISetTopBoxControls SetTopBox; + DevicePresetsView PresetsView; + + public SetTopBoxMediumPageManager(ISetTopBoxControls stb, BasicTriListWithSmartObject trilist) + : base(stb.DisplayUiType) + { + SetTopBox = stb; + TriList = trilist; + if(stb.PresetsModel != null) + PresetsView = new DevicePresetsView(trilist, stb.PresetsModel); + } + + public override void Show() + { + if(PresetsView != null) + PresetsView.Attach(); + uint offset = GetOffsetJoin(); + if (SetTopBox.HasDvr) // Show backing page with DVR controls + { + BackingPageJoin = offset + 1; + AllLeftSubpages = new uint[] { 6, 7, 8 }; + } + else // Show the backing page with no DVR controls + { + BackingPageJoin = offset + 2; + AllLeftSubpages = new uint[] { 6, 7 }; + } + + if (LeftSubpageJoin == 0) + LeftSubpageJoin = offset + 6; // default to presets + TriList.BooleanInput[BackingPageJoin].BoolValue = true; + TriList.BooleanInput[LeftSubpageJoin].BoolValue = true; + + // Attach buttons to interlock + foreach(var p in AllLeftSubpages) + { + var p2 = p; // scope + TriList.SetSigFalseAction(10000 + p2, () => InterlockLeftSubpage(p2)); + } + } + + public override void Hide() + { + TriList.BooleanInput[BackingPageJoin].BoolValue = false; + TriList.BooleanInput[LeftSubpageJoin].BoolValue = false; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/SinglePageManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/SinglePageManager.cs new file mode 100644 index 00000000..dd7d605c --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/UI PageManagers/SinglePageManager.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Crestron.SimplSharpPro.DeviceSupport; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core.PageManagers +{ + /// + /// A simple class that hides and shows the default subpage for a given source type + /// + public class SinglePageManager : PageManager + { + BasicTriList TriList; + uint BackingPageJoin; + + public SinglePageManager(uint pageJoin, BasicTriList trilist) + { + TriList = trilist; + BackingPageJoin = pageJoin; + } + + public override void Show() + { + TriList.BooleanInput[BackingPageJoin].BoolValue = true; + } + + public override void Hide() + { + TriList.BooleanInput[BackingPageJoin].BoolValue = false; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/VideoStatus/VideoStatusCues.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/VideoStatus/VideoStatusCues.cs new file mode 100644 index 00000000..5db78fb5 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/VideoStatus/VideoStatusCues.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +namespace PepperDash.Essentials.Core +{ + public static class VideoStatusCues + { + public static readonly Cue HasVideoStatusFeedback = Cue.BoolCue("HasVideoStatusFeedback", 1); + public static readonly Cue VideoSyncFeedback = Cue.BoolCue("VideoSyncFeedback", 2); + public static readonly Cue HdcpActiveFeedback = Cue.BoolCue("HdcpActiveFeedback", 3); + public static readonly Cue HdcpStateFeedback = Cue.StringCue("HdcpStateFeedback", 3); + public static readonly Cue VideoResolutionFeedback = Cue.StringCue("VideoResolutionFeedback", 2); + public static readonly Cue VideoStatusDeviceKey = Cue.StringCue("VideoStatusDeviceKey", 0); + public static readonly Cue VideoStatusDeviceName = Cue.StringCue("VideoStatusDeviceName", 4); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/VideoStatus/VideoStatusOutputs.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/VideoStatus/VideoStatusOutputs.cs new file mode 100644 index 00000000..62d0040a --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/VideoStatus/VideoStatusOutputs.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; + +namespace PepperDash.Essentials.Core +{ + /// + /// Use this class to pass in values to RoutingInputPorts. Unused properties will have default + /// funcs assigned to them. + /// + public class VideoStatusFuncsWrapper + { + public Func HasVideoStatusFunc { get; set; } + public Func HdcpActiveFeedbackFunc { get; set; } + public Func HdcpStateFeedbackFunc { get; set; } + public Func VideoResolutionFeedbackFunc { get; set; } + public Func VideoSyncFeedbackFunc { get; set; } + + public VideoStatusFuncsWrapper() + { + HasVideoStatusFunc = () => true; + HdcpActiveFeedbackFunc = () => false; + HdcpStateFeedbackFunc = () => ""; + VideoResolutionFeedbackFunc = () => "n/a"; + VideoSyncFeedbackFunc = () => false; + } + } + + /// + /// Wraps up the common video statuses exposed on a video input port + /// + public class VideoStatusOutputs + { + public BoolFeedback HasVideoStatusFeedback { get; private set; } + public BoolFeedback HdcpActiveFeedback { get; private set; } + public StringFeedback HdcpStateFeedback { get; private set; } + public StringFeedback VideoResolutionFeedback { get; private set; } + public BoolFeedback VideoSyncFeedback { get; private set; } + + /// + /// Gets the default, empty status group. + /// + public static VideoStatusOutputs NoStatus { get { return _Default; } } + static VideoStatusOutputs _Default = new VideoStatusOutputs(new VideoStatusFuncsWrapper + { + HasVideoStatusFunc = () => false + }); + + public VideoStatusOutputs(VideoStatusFuncsWrapper funcs) + { + HasVideoStatusFeedback = new BoolFeedback("HasVideoStatusFeedback", funcs.HasVideoStatusFunc); + HdcpActiveFeedback = new BoolFeedback("HdcpActiveFeedback", funcs.HdcpActiveFeedbackFunc); + HdcpStateFeedback = new StringFeedback("HdcpStateFeedback", funcs.HdcpStateFeedbackFunc); + VideoResolutionFeedback = new StringFeedback("VideoResolutionFeedback", + funcs.VideoResolutionFeedbackFunc); + VideoSyncFeedback = new BoolFeedback("VideoSyncFeedback", funcs.VideoSyncFeedbackFunc); + } + + public void FireAll() + { + HasVideoStatusFeedback.FireUpdate(); + HdcpActiveFeedback.FireUpdate(); + HdcpActiveFeedback.FireUpdate(); + VideoResolutionFeedback.FireUpdate(); + VideoSyncFeedback.FireUpdate(); + } + + public List ToList() + { + return new List + { + HasVideoStatusFeedback, + HdcpActiveFeedback, + HdcpStateFeedback, + VideoResolutionFeedback, + VideoSyncFeedback + }; + } + } + + ///// + ///// Wraps up the common video statuses exposed on a video input port + ///// + //public class BasicVideoStatus : IBasicVideoStatus + //{ + // public event VideoStatusChangeHandler VideoStatusChange; + + // public bool HasVideoStatus { get; private set; } + + // public bool HdcpActive + // { + // get { return HdcpActiveFunc != null ? HdcpActiveFunc() : false; } + // } + + // public string HdcpState + // { + // get { return HdcpStateFunc != null? HdcpStateFunc() : ""; } + // } + + // public string VideoResolution + // { + // get { return VideoResolutionFunc != null ? VideoResolutionFunc() : ""; } + // } + + // public bool VideoSync + // { + // get { return VideoSyncFunc != null ? VideoSyncFunc() : false; } + // } + + // Func HdcpActiveFunc; + // Func HdcpStateFunc; + // Func VideoResolutionFunc; + // Func VideoSyncFunc; + + // public BasicVideoStatus(bool hasVideoStatus, Func hdcpActiveFunc, + // Func hdcpStateFunc, Func videoResolutionFunc, Func videoSyncFunc) + // { + // HasVideoStatus = hasVideoStatus; + // HdcpActiveFunc = hdcpActiveFunc; + // HdcpStateFunc = hdcpStateFunc; + // VideoResolutionFunc = videoResolutionFunc; + // VideoSyncFunc = videoSyncFunc; + // } + //} + + //public enum eVideoStatusChangeType + //{ + // HdcpActive, + // HdcpState, + // VideoResolution, + // VideoSync + //} + + //public interface IBasicVideoStatus + //{ + // event VideoStatusChangeHandler VideoStatusChange; + // bool HdcpActive { get; } + // string HdcpState { get; } + // string VideoResolution { get; } + // bool VideoSync { get; } + //} + + //public delegate void VideoStatusChangeHandler(IBasicVideoStatus device, eVideoStatusChangeType type); +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/app.config b/essentials-framework/Essentials Core/PepperDashEssentialsBase/app.config new file mode 100644 index 00000000..1e79e7b5 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/essentials-framework/Essentials DM/Essentials_DM.sln b/essentials-framework/Essentials DM/Essentials_DM.sln new file mode 100644 index 00000000..fa874f80 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Essentials_DM", "Essentials_DM\Essentials_DM.csproj", "{9199CE8A-0C9F-4952-8672-3EED798B284F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9199CE8A-0C9F-4952-8672-3EED798B284F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9199CE8A-0C9F-4952-8672-3EED798B284F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9199CE8A-0C9F-4952-8672-3EED798B284F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9199CE8A-0C9F-4952-8672-3EED798B284F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/essentials-framework/Essentials DM/Essentials_DM/AirMedia/AirMediaController.cs b/essentials-framework/Essentials DM/Essentials_DM/AirMedia/AirMediaController.cs new file mode 100644 index 00000000..b4ee9784 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/AirMedia/AirMediaController.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.DeviceSupport.Support; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.AirMedia; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.DM.AirMedia +{ + public class AirMediaController : CrestronGenericBaseDevice, IRoutingInputsOutputs, IIROutputPorts, IComPorts + { + public AmX00 AirMedia { get; private set; } + + public DeviceConfig DeviceConfig { get; private set; } + + AirMediaPropertiesConfig PropertiesConfig; + + public RoutingPortCollection InputPorts { get; private set; } + + public RoutingPortCollection OutputPorts { get; private set; } + + public BoolFeedback IsInSessionFeedback { get; private set; } + public IntFeedback ErrorFeedback { get; private set; } + public IntFeedback NumberOfUsersConnectedFeedback { get; set; } + public IntFeedback LoginCodeFeedback { get; set; } + public StringFeedback ConnectionAddressFeedback { get; set; } + public StringFeedback HostnameFeedback { get; set; } + public IntFeedback VideoOutFeedback { get; private set; } + public BoolFeedback HdmiVideoSyncDetectedFeedback { get; private set; } + public StringFeedback SerialNumberFeedback { get; private set; } + public BoolFeedback AutomaticInputRoutingEnabledFeedback { get; private set; } + + public AirMediaController(string key, string name, AmX00 device, DeviceConfig dc, AirMediaPropertiesConfig props) + :base(key, name, device) + { + AirMedia = device; + + DeviceConfig = dc; + + PropertiesConfig = props; + + InputPorts.Add(new RoutingInputPort(DmPortName.Osd, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.None, new Action(SelectPinPointUxLandingPage), this)); + + InputPorts.Add(new RoutingInputPort(DmPortName.AirMediaIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Streaming, new Action(SelectAirMedia), this)); + + InputPorts.Add(new RoutingInputPort(DmPortName.HdmiIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(SelectHdmiIn), this)); + + InputPorts.Add(new RoutingInputPort(DmPortName.AirBoardIn, eRoutingSignalType.Video, + eRoutingPortConnectionType.None, new Action(SelectAirboardIn), this)); + + if (AirMedia is Am300) + { + InputPorts.Add(new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, new Action(SelectDmIn), this)); + } + + AirMedia.AirMedia.AirMediaChange += new Crestron.SimplSharpPro.DeviceSupport.GenericEventHandler(AirMedia_AirMediaChange); + + IsInSessionFeedback = new BoolFeedback( new Func(() => AirMedia.AirMedia.StatusFeedback.UShortValue == 0 )); + ErrorFeedback = new IntFeedback(new Func(() => AirMedia.AirMedia.ErrorFeedback.UShortValue)); + NumberOfUsersConnectedFeedback = new IntFeedback(new Func(() => AirMedia.AirMedia.NumberOfUsersConnectedFeedback.UShortValue)); + LoginCodeFeedback = new IntFeedback(new Func(() => AirMedia.AirMedia.LoginCodeFeedback.UShortValue)); + ConnectionAddressFeedback = new StringFeedback(new Func(() => AirMedia.AirMedia.ConnectionAddressFeedback.StringValue)); + HostnameFeedback = new StringFeedback(new Func(() => AirMedia.AirMedia.HostNameFeedback.StringValue)); + + // TODO: Figure out if we can actually get the TSID/Serial + SerialNumberFeedback = new StringFeedback(new Func(() => "unknown")); + + AirMedia.DisplayControl.DisplayControlChange += new Crestron.SimplSharpPro.DeviceSupport.GenericEventHandler(DisplayControl_DisplayControlChange); + + VideoOutFeedback = new IntFeedback(new Func(() => Convert.ToInt16(AirMedia.DisplayControl.VideoOutFeedback))); + AutomaticInputRoutingEnabledFeedback = new BoolFeedback(new Func(() => AirMedia.DisplayControl.EnableAutomaticRoutingFeedback.BoolValue)); + + AirMedia.HdmiIn.StreamChange += new Crestron.SimplSharpPro.DeviceSupport.StreamEventHandler(HdmiIn_StreamChange); + + HdmiVideoSyncDetectedFeedback = new BoolFeedback(new Func(() => AirMedia.HdmiIn.SyncDetectedFeedback.BoolValue)); + } + + public override bool CustomActivate() + { + if (PropertiesConfig.AutoSwitchingEnabled) + AirMedia.DisplayControl.EnableAutomaticRouting(); + else + AirMedia.DisplayControl.DisableAutomaticRouting(); + + return base.CustomActivate(); + } + + void AirMedia_AirMediaChange(object sender, Crestron.SimplSharpPro.DeviceSupport.GenericEventArgs args) + { + if (args.EventId == AirMediaInputSlot.AirMediaStatusFeedbackEventId) + IsInSessionFeedback.FireUpdate(); + else if (args.EventId == AirMediaInputSlot.AirMediaErrorFeedbackEventId) + ErrorFeedback.FireUpdate(); + else if (args.EventId == AirMediaInputSlot.AirMediaNumberOfUserConnectedEventId) + NumberOfUsersConnectedFeedback.FireUpdate(); + else if (args.EventId == AirMediaInputSlot.AirMediaLoginCodeEventId) + LoginCodeFeedback.FireUpdate(); + else if (args.EventId == AirMediaInputSlot.AirMediaConnectionAddressFeedbackEventId) + ConnectionAddressFeedback.FireUpdate(); + else if (args.EventId == AirMediaInputSlot.AirMediaHostNameFeedbackEventId) + HostnameFeedback.FireUpdate(); + } + + void DisplayControl_DisplayControlChange(object sender, Crestron.SimplSharpPro.DeviceSupport.GenericEventArgs args) + { + if (args.EventId == AmX00.VideoOutFeedbackEventId) + VideoOutFeedback.FireUpdate(); + else if (args.EventId == AmX00.EnableAutomaticRoutingFeedbackEventId) + AutomaticInputRoutingEnabledFeedback.FireUpdate(); + } + + void HdmiIn_StreamChange(Crestron.SimplSharpPro.DeviceSupport.Stream stream, Crestron.SimplSharpPro.DeviceSupport.StreamEventArgs args) + { + if (args.EventId == DMInputEventIds.SourceSyncEventId) + HdmiVideoSyncDetectedFeedback.FireUpdate(); + } + + /// + /// Sets the VideoOut source ( 0 = PinpointUX, 1 = AirMedia, 2 = HDMI, 3 = DM, 4 = Airboard ) + /// + /// source number + public void SelectVideoOut(uint source) + { + AirMedia.DisplayControl.VideoOut = (AmX00DisplayControl.eAirMediaX00VideoSource)source; + } + + /// + /// Selects the PinPointUXLandingPage input + /// + public void SelectPinPointUxLandingPage() + { + AirMedia.DisplayControl.VideoOut = AmX00DisplayControl.eAirMediaX00VideoSource.PinPointUxLandingPage; + } + + /// + /// Selects the AirMedia input + /// + public void SelectAirMedia() + { + AirMedia.DisplayControl.VideoOut = AmX00DisplayControl.eAirMediaX00VideoSource.AirMedia; + } + + /// + /// Selects the DM input + /// + public void SelectDmIn() + { + AirMedia.DisplayControl.VideoOut = AmX00DisplayControl.eAirMediaX00VideoSource.HDMI; + } + + /// + /// Selects the HDMI INput + /// + public void SelectHdmiIn() + { + AirMedia.DisplayControl.VideoOut = AmX00DisplayControl.eAirMediaX00VideoSource.DM; + } + + public void SelectAirboardIn() + { + AirMedia.DisplayControl.VideoOut = AmX00DisplayControl.eAirMediaX00VideoSource.AirBoard; + } + + /// + /// Reboots the device + /// + public void RebootDevice() + { + AirMedia.AirMedia.DeviceReboot(); + } + + #region IIROutputPorts Members + + public CrestronCollection IROutputPorts + { + get { return AirMedia.IROutputPorts; } + } + + public int NumberOfIROutputPorts + { + get { return AirMedia.NumberOfIROutputPorts; } + } + + + + #endregion + + + + #region IComPorts Members + + public CrestronCollection ComPorts + { + get { return AirMedia.ComPorts; } + } + + public int NumberOfComPorts + { + get { return AirMedia.NumberOfComPorts; } + } + + #endregion + + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/AirMedia/AirMediaPropertiesConfig.cs b/essentials-framework/Essentials DM/Essentials_DM/AirMedia/AirMediaPropertiesConfig.cs new file mode 100644 index 00000000..b89bd989 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/AirMedia/AirMediaPropertiesConfig.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.DM.AirMedia +{ + public class AirMediaPropertiesConfig + { + [JsonProperty("control")] + public ControlPropertiesConfig Control { get; set; } + + [JsonProperty("autoSwitching")] + public bool AutoSwitchingEnabled { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmInputCardBase.cs b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmInputCardBase.cs new file mode 100644 index 00000000..a0c9536c --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmInputCardBase.cs @@ -0,0 +1,62 @@ +//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.Cards; + +//using PepperDash.Essentials.Core; +//using PepperDash.Essentials.DM; + +//namespace PepperDash.Essentials.DM.Cards +//{ +// public class DmInputCardControllerBase : IRoutingInputsOutputs +// { +// public string Key { get; private set; } +// public uint Slot { get; private set; } +// public abstract eDmInputCardType Type { get; } + +// //public RoutingOutputPort BackplaneVideoOut { get; private set; } +// //public RoutingOutputPort BackplaneAudioOut { get; private set; } + +// public RoutingPortCollection InputPorts { get; private set; } + +// public RoutingPortCollection OutputPorts { get; private set; } + +// public DmInputCardControllerBase(string key, uint slot) +// { +// Key = key; +// Slot = slot; +// //BackplaneAudioOut = new RoutingOutputPort("backplaneAudioOut", eRoutingSignalType.Audio, +// // eRoutingPortConnectionType.BackplaneOnly, slot, this); +// //BackplaneVideoOut = new RoutingOutputPort("backplaneVideoOut", eRoutingSignalType.Video, +// // eRoutingPortConnectionType.BackplaneOnly, slot, this); +// //InputPorts = new RoutingPortCollection(); +// //OutputPorts = new RoutingPortCollection { BackplaneAudioOut, BackplaneVideoOut }; +// } + +// ///// +// ///// Gets a physical port by name. Returns null if doesn't exist +// ///// +// //public RoutingInputPort GetInputPort(string key) +// //{ +// // return InputPorts.FirstOrDefault(p => p.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); +// //} + +// ///// +// ///// Gets a physical port by name. Returns null if doesn't exist +// ///// +// //public RoutingOutputPort GetOutputPort(string key) +// //{ +// // return OutputPorts.FirstOrDefault(p => p.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); +// //} +// } + +// public enum eDmInputCardType +// { +// None, DmcHd, DmcHdDsp, Dmc4kHd, Dmc4kHdDsp, Dmc4kC, Dmc4kCDsp +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmOutputCardBase.cs b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmOutputCardBase.cs new file mode 100644 index 00000000..c554539a --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmOutputCardBase.cs @@ -0,0 +1,79 @@ +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.Cards; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.DM; + +namespace PepperDash.Essentials.DM.Cards +{ + /// + /// + /// + public abstract class DmSingleOutputCardControllerBase// : IRoutingInputsOutputs + { + public string Key { get; private set; } + public uint Slot { get; private set; } + public abstract eDmOutputCardType Type { get; } + + //public RoutingInputPort BackplaneAudioIn1 { get; private set; } + //public RoutingInputPort BackplaneVideoIn1 { get; private set; } + //public RoutingInputPort BackplaneAudioIn2 { get; private set; } + //public RoutingInputPort BackplaneVideoIn2 { get; private set; } + + //public RoutingPortCollection InputPorts { get; private set; } + //public RoutingPortCollection OutputPorts { get; private set; } + + public DmSingleOutputCardControllerBase(string key, uint cardSlot) + { + Key = key; + Slot = cardSlot; + //BackplaneAudioIn1 = new RoutingInputPort("backplaneAudioIn1", eRoutingSignalType.Audio, + // eRoutingPortConnectionType.BackplaneOnly, cardSlot, this); + //BackplaneVideoIn1 = new RoutingInputPort("backplaneVideoIn1", eRoutingSignalType.Video, + // eRoutingPortConnectionType.BackplaneOnly, cardSlot, this); + //BackplaneAudioIn2 = new RoutingInputPort("backplaneAudioIn2", eRoutingSignalType.Audio, + // eRoutingPortConnectionType.BackplaneOnly, cardSlot + 1, this); + //BackplaneVideoIn2 = new RoutingInputPort("backplaneVideoIn2", eRoutingSignalType.Video, + // eRoutingPortConnectionType.BackplaneOnly, cardSlot + 1, this); + //InputPorts = new RoutingPortCollection + //{ + // BackplaneAudioIn1, + // BackplaneAudioIn2, + // BackplaneVideoIn1, + // BackplaneVideoIn2 + //}; + //OutputPorts = new RoutingPortCollection(); + } + + ///// + ///// Gets a physical port by name. Returns null if doesn't exist + ///// + //public RoutingInputPort GetInputPort(string key) + //{ + // return InputPorts.FirstOrDefault(p => p.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); + //} + + ///// + ///// Gets a physical port by name. Returns null if doesn't exist + ///// + //public RoutingOutputPort GetOutputPort(string key) + //{ + // return OutputPorts.FirstOrDefault(p => p.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); + //} + } + + /// + /// + /// + public enum eDmOutputCardType + { + None, Dmc4kCoHd, Dmc4kHdo, DmcCoHd, DmcSoHd + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/Dmc4kCoHdSingleOutputCard.cs b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/Dmc4kCoHdSingleOutputCard.cs new file mode 100644 index 00000000..3e3fd309 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/Dmc4kCoHdSingleOutputCard.cs @@ -0,0 +1,43 @@ +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.Cards; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.DM; + +namespace PepperDash.Essentials.DM.Cards +{ + public class Dmc4kCoHdSingleOutputCard : DmSingleOutputCardControllerBase + { + public override eDmOutputCardType Type + { + get { return eDmOutputCardType.Dmc4kCoHd; } + } + public Dmc4kCoHdSingle Card { get; private set; } + + //public RoutingOutputPort DmOut1 { get; private set; } + //public RoutingOutputPort DmOut2 { get; private set; } + //public RoutingOutputPort HdmiOut1 { get; private set; } + + public Dmc4kCoHdSingleOutputCard(string key, Dmc4kCoHdSingle card, uint slot) + : base(key, slot) + { + Card = card; + //DmOut1 = new RoutingOutputPort(DmPortName.DmOut1, eRoutingSignalType.AudioVideo, + // eRoutingPortConnectionType.DmCat, null, this); + //DmOut2 = new RoutingOutputPort(DmPortName.DmOut2, eRoutingSignalType.AudioVideo, + // eRoutingPortConnectionType.DmCat, null, this); + //HdmiOut1 = new RoutingOutputPort(DmPortName.HdmiOut1, eRoutingSignalType.AudioVideo, + // eRoutingPortConnectionType.Hdmi, null, this); + + //OutputPorts.AddRange(new[] { DmOut1, DmOut2, HdmiOut1 }); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/Dmc4kHdoSingleOutputCard.cs b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/Dmc4kHdoSingleOutputCard.cs new file mode 100644 index 00000000..b15c4006 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/Dmc4kHdoSingleOutputCard.cs @@ -0,0 +1,46 @@ +//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.Cards; + +//using PepperDash.Core; +//using PepperDash.Essentials.Core; +//using PepperDash.Essentials.DM; + +//namespace PepperDash.Essentials.DM.Cards +//{ +// public class Dmc4kHdoSingleOutputCard : DmSingleOutputCardControllerBase +// { +// public override eDmOutputCardType Type +// { +// get { return eDmOutputCardType.Dmc4kHdo; } +// } +// public Dmc4kHdoSingle Card { get; private set; } + +// //public RoutingOutputPort AudioOut1 { get; private set; } +// //public RoutingOutputPort AudioOut2 { get; private set; } +// //public RoutingOutputPort HdmiOut1 { get; private set; } +// //public RoutingOutputPort HdmiOut2 { get; private set; } + +// public Dmc4kHdoSingleOutputCard(string key, Dmc4kHdoSingle card, uint slot) +// : base(key, slot) +// { +// Card = card; +// //AudioOut1 = new RoutingOutputPort(DmPortName.BalancedAudioOut1, eRoutingSignalType.Audio, +// // eRoutingPortConnectionType.LineAudio, null, this); +// //AudioOut2 = new RoutingOutputPort(DmPortName.BalancedAudioOut2, eRoutingSignalType.Audio, +// // eRoutingPortConnectionType.LineAudio, null, this); +// //HdmiOut1 = new RoutingOutputPort(DmPortName.HdmiOut1, eRoutingSignalType.AudioVideo, +// // eRoutingPortConnectionType.Hdmi, null, this); +// //HdmiOut2 = new RoutingOutputPort(DmPortName.HdmiOut2, eRoutingSignalType.AudioVideo, +// // eRoutingPortConnectionType.Hdmi, null, this); + +// //OutputPorts.AddRange(new[] { AudioOut1, AudioOut2, HdmiOut1, HdmiOut2 }); +// } +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmcC4kInputCard.cs b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmcC4kInputCard.cs new file mode 100644 index 00000000..40f6044c --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmcC4kInputCard.cs @@ -0,0 +1,76 @@ +//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.Cards; + +//using PepperDash.Essentials.Core; +//using PepperDash.Essentials.DM; + +//namespace PepperDash.Essentials.DM.Cards +//{ +// public class Dmc4kCController : DmInputCardControllerBase +// { +// public override eDmInputCardType Type +// { +// get { return eDmInputCardType.Dmc4kC; } +// } +// public Dmc4kC Card { get; private set; } + +// //public RoutingInputPortWithVideoStatuses DmIn { get; private set; } +// //public RoutingOutputPort HdmiLoopOut { get; private set; } +// //public RoutingOutputPort AudioLoopOut { get; private set; } + +// public Dmc4kCController(string key, Dmc4kC card, uint slot) +// : base(key, slot) +// { +// Card = card; +// //DmIn = new RoutingInputPortWithVideoStatuses(DmPortName.DmIn, eRoutingSignalType.AudioVideo, +// // eRoutingPortConnectionType.DmCat, null, this, +// // VideoStatusHelper.GetDmInputStatusFuncs(Card.DmInput)); + +// //HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.AudioVideo, +// // eRoutingPortConnectionType.Hdmi, null, this); +// //AudioLoopOut = new RoutingOutputPort(DmPortName.AudioLoopOut, eRoutingSignalType.Audio, +// // eRoutingPortConnectionType.Hdmi, null, this); + +// //InputPorts.Add(DmIn); +// //OutputPorts.AddRange(new[] { HdmiLoopOut, AudioLoopOut }); +// } +// } + +// public class Dmc4kCDspController : DmInputCardControllerBase +// { +// public override eDmInputCardType Type +// { +// get { return eDmInputCardType.Dmc4kCDsp; } +// } +// public Dmc4kCDsp Card { get; private set; } + +// //public RoutingInputPortWithVideoStatuses DmIn { get; private set; } +// //public RoutingOutputPort HdmiLoopOut { get; private set; } +// //public RoutingOutputPort AudioLoopOut { get; private set; } + +// public Dmc4kCDspController(string key, Dmc4kCDsp card, uint slot) +// : base(key, slot) +// { +// Card = card; +// //DmIn = new RoutingInputPortWithVideoStatuses(DmPortName.DmIn, eRoutingSignalType.AudioVideo, +// // eRoutingPortConnectionType.DmCat, null, this, +// // VideoStatusHelper.GetDmInputStatusFuncs(Card.DmInput)); + +// //HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.AudioVideo, +// // eRoutingPortConnectionType.Hdmi, null, this); +// //AudioLoopOut = new RoutingOutputPort(DmPortName.AudioLoopOut, eRoutingSignalType.Audio, +// // eRoutingPortConnectionType.Hdmi, null, this); + +// //InputPorts.Add(DmIn); +// //OutputPorts.AddRange(new[] { HdmiLoopOut, AudioLoopOut }); +// } +// } + +//} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmcHD4kInputCard.cs b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmcHD4kInputCard.cs new file mode 100644 index 00000000..e317085c --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Cards REMOVE/DmcHD4kInputCard.cs @@ -0,0 +1,82 @@ +//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.Cards; + +//using PepperDash.Essentials.Core; +//using PepperDash.Essentials.DM; + +//namespace PepperDash.Essentials.DM.Cards +//{ +// /// +// /// +// /// +// public class Dmc4kHdController : DmInputCardControllerBase +// { +// public Dmc4kHd Card { get; private set; } +// public override eDmInputCardType Type +// { +// get { return eDmInputCardType.Dmc4kHd; } +// } + +// public RoutingInputPortWithVideoStatuses HdmiIn { get; private set; } +// public RoutingOutputPort HdmiLoopOut { get; private set; } +// public RoutingOutputPort AudioLoopOut { get; private set; } + +// public Dmc4kHdController(string key, Dmc4kHd card, uint slot) +// : base(key, slot) +// { +// Card = card; +// HdmiIn = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn, eRoutingSignalType.AudioVideo, +// eRoutingPortConnectionType.Hdmi, null, this, +// VideoStatusHelper.GetHdmiInputStatusFuncs(Card.HdmiInput)); + +// HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.AudioVideo, +// eRoutingPortConnectionType.Hdmi, null, this); +// AudioLoopOut = new RoutingOutputPort(DmPortName.AudioLoopOut, eRoutingSignalType.Audio, +// eRoutingPortConnectionType.Hdmi, null, this); + +// InputPorts.Add(HdmiIn); +// OutputPorts.AddRange(new[] { HdmiLoopOut, AudioLoopOut }); +// } +// } + +// /// +// /// +// /// +// public class Dmc4kHdDspController : DmInputCardControllerBase +// { +// public Dmc4kHdDsp Card { get; private set; } +// public override eDmInputCardType Type +// { +// get { return eDmInputCardType.Dmc4kHdDsp; } +// } + +// //public RoutingInputPortWithVideoStatuses HdmiIn { get; private set; } +// //public RoutingOutputPort HdmiLoopOut { get; private set; } +// //public RoutingOutputPort AudioLoopOut { get; private set; } + +// public Dmc4kHdDspController(string key, Dmc4kHdDsp card, uint slot) +// : base(key, slot) +// { +// Card = card; +// //HdmiIn = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn, eRoutingSignalType.AudioVideo, +// // eRoutingPortConnectionType.Hdmi, null, this, +// // VideoStatusHelper.GetHdmiInputStatusFuncs(Card.HdmiInput)); + +// //HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.AudioVideo, +// // eRoutingPortConnectionType.Hdmi, null, this); +// //AudioLoopOut = new RoutingOutputPort(DmPortName.AudioLoopOut, eRoutingSignalType.Audio, +// // eRoutingPortConnectionType.Hdmi, null, this); + +// //InputPorts.Add(HdmiIn); +// //OutputPorts.AddRange(new[] { HdmiLoopOut, AudioLoopOut }); +// } +// } +//} + diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/._DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/._DmChassisController.cs new file mode 100644 index 00000000..a5ed710c Binary files /dev/null and b/essentials-framework/Essentials DM/Essentials_DM/Chassis/._DmChassisController.cs differ diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput.cs new file mode 100644 index 00000000..4b4a927e --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DM; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + public class DmCardAudioOutputController : IBasicVolumeWithFeedback + { + public Audio.Output Output { get; private set; } + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public BoolFeedback MuteFeedback { get; private set; } + + ushort PreMuteVolumeLevel; + bool IsMuted; + + public DmCardAudioOutputController(Audio.Output output) + { + Output = output; + VolumeLevelFeedback = new IntFeedback(() => Output.VolumeFeedback.UShortValue); + MuteFeedback = new BoolFeedback(() => IsMuted); + } + + #region IBasicVolumeWithFeedback Members + + /// + /// + /// + public void MuteOff() + { + SetVolume(PreMuteVolumeLevel); + IsMuted = false; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void MuteOn() + { + PreMuteVolumeLevel = Output.VolumeFeedback.UShortValue; + SetVolume(0); + IsMuted = true; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void SetVolume(ushort level) + { + Debug.Console(2, "Set volume out {0}", level); + Output.Volume.UShortValue = level; + } + + /// + /// + /// + internal void VolumeEventFromChassis() + { + VolumeLevelFeedback.FireUpdate(); + } + + #endregion + + #region IBasicVolumeControls Members + + /// + /// + /// + public void MuteToggle() + { + if (IsMuted) + MuteOff(); + else + MuteOn(); + } + + /// + /// + /// + public void VolumeDown(bool pressRelease) + { + if (pressRelease) + { + var remainingRatio = Output.Volume.UShortValue / 65535; + Output.Volume.CreateRamp(0, (uint)(400 * remainingRatio)); + } + else + Output.Volume.StopRamp(); + } + + /// + /// + /// + public void VolumeUp(bool pressRelease) + { + if (pressRelease) + { + var remainingRatio = (65535 - Output.Volume.UShortValue) / 65535; + Output.Volume.CreateRamp(65535, 400); + } + else + Output.Volume.StopRamp(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput.cs.orig b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput.cs.orig new file mode 100644 index 00000000..acaf7916 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput.cs.orig @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DM; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + public class DmCardAudioOutputController : IBasicVolumeWithFeedback + { + public Audio.Output Output { get; private set; } + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public BoolFeedback MuteFeedback { get; private set; } + + ushort PreMuteVolumeLevel; + bool IsMuted; + + public DmCardAudioOutputController(Audio.Output output) + { + Output = output; + VolumeLevelFeedback = new IntFeedback(() => Output.VolumeFeedback.UShortValue); + MuteFeedback = new BoolFeedback(() => IsMuted); + } + + #region IBasicVolumeWithFeedback Members + + /// + /// + /// + public void MuteOff() + { + SetVolume(PreMuteVolumeLevel); + IsMuted = false; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void MuteOn() + { + PreMuteVolumeLevel = Output.VolumeFeedback.UShortValue; + SetVolume(0); + IsMuted = true; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void SetVolume(ushort level) + { + Debug.Console(2, "Set volume out {0}", level); + Output.Volume.UShortValue = level; + } + + /// + /// + /// + internal void VolumeEventFromChassis() + { + VolumeLevelFeedback.FireUpdate(); + } + + #endregion + + #region IBasicVolumeControls Members + + /// + /// + /// + public void MuteToggle() + { + if (IsMuted) + MuteOff(); + else + MuteOn(); + } + + /// + /// + /// + public void VolumeDown(bool pressRelease) + { + if (pressRelease) +<<<<<<< HEAD + { + var remainingRatio = Output.Volume.UShortValue / 65535; + Output.Volume.CreateRamp(0, (uint)(400 * remainingRatio)); + } +======= + Output.Volume.CreateRamp(0, 400); +>>>>>>> origin/feature/fusion-nyu + else + Output.Volume.StopRamp(); + } + + /// + /// + /// + public void VolumeUp(bool pressRelease) + { + if (pressRelease) + { + var remainingRatio = (65535 - Output.Volume.UShortValue) / 65535; + Output.Volume.CreateRamp(65535, 400); + } + else + Output.Volume.StopRamp(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_BACKUP_14408.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_BACKUP_14408.cs new file mode 100644 index 00000000..acaf7916 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_BACKUP_14408.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DM; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + public class DmCardAudioOutputController : IBasicVolumeWithFeedback + { + public Audio.Output Output { get; private set; } + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public BoolFeedback MuteFeedback { get; private set; } + + ushort PreMuteVolumeLevel; + bool IsMuted; + + public DmCardAudioOutputController(Audio.Output output) + { + Output = output; + VolumeLevelFeedback = new IntFeedback(() => Output.VolumeFeedback.UShortValue); + MuteFeedback = new BoolFeedback(() => IsMuted); + } + + #region IBasicVolumeWithFeedback Members + + /// + /// + /// + public void MuteOff() + { + SetVolume(PreMuteVolumeLevel); + IsMuted = false; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void MuteOn() + { + PreMuteVolumeLevel = Output.VolumeFeedback.UShortValue; + SetVolume(0); + IsMuted = true; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void SetVolume(ushort level) + { + Debug.Console(2, "Set volume out {0}", level); + Output.Volume.UShortValue = level; + } + + /// + /// + /// + internal void VolumeEventFromChassis() + { + VolumeLevelFeedback.FireUpdate(); + } + + #endregion + + #region IBasicVolumeControls Members + + /// + /// + /// + public void MuteToggle() + { + if (IsMuted) + MuteOff(); + else + MuteOn(); + } + + /// + /// + /// + public void VolumeDown(bool pressRelease) + { + if (pressRelease) +<<<<<<< HEAD + { + var remainingRatio = Output.Volume.UShortValue / 65535; + Output.Volume.CreateRamp(0, (uint)(400 * remainingRatio)); + } +======= + Output.Volume.CreateRamp(0, 400); +>>>>>>> origin/feature/fusion-nyu + else + Output.Volume.StopRamp(); + } + + /// + /// + /// + public void VolumeUp(bool pressRelease) + { + if (pressRelease) + { + var remainingRatio = (65535 - Output.Volume.UShortValue) / 65535; + Output.Volume.CreateRamp(65535, 400); + } + else + Output.Volume.StopRamp(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_BASE_14408.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_BASE_14408.cs new file mode 100644 index 00000000..3a069c39 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_BASE_14408.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DM; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + public class DmCardAudioOutputController : IBasicVolumeWithFeedback + { + public Audio.Output Output { get; private set; } + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public BoolFeedback MuteFeedback { get; private set; } + + ushort PreMuteVolumeLevel; + bool IsMuted; + + public DmCardAudioOutputController(Audio.Output output) + { + Output = output; + VolumeLevelFeedback = new IntFeedback(() => Output.VolumeFeedback.UShortValue); + MuteFeedback = new BoolFeedback(() => IsMuted); + } + + #region IBasicVolumeWithFeedback Members + + /// + /// + /// + public void MuteOff() + { + SetVolume(PreMuteVolumeLevel); + IsMuted = false; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void MuteOn() + { + PreMuteVolumeLevel = Output.VolumeFeedback.UShortValue; + SetVolume(0); + IsMuted = true; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void SetVolume(ushort level) + { + Debug.Console(2, "Set volume out {0}", level); + Output.Volume.UShortValue = level; + } + + /// + /// + /// + internal void VolumeEventFromChassis() + { + VolumeLevelFeedback.FireUpdate(); + } + + #endregion + + #region IBasicVolumeControls Members + + /// + /// + /// + public void MuteToggle() + { + if (IsMuted) + MuteOff(); + else + MuteOn(); + } + + /// + /// + /// + public void VolumeDown(bool pressRelease) + { + if (pressRelease) + Output.Volume.CreateRamp(0, 400); +#warning SCALE THIS RAMP + else + Output.Volume.StopRamp(); + } + + /// + /// + /// + public void VolumeUp(bool pressRelease) + { + if (pressRelease) + Output.Volume.CreateRamp(65535, 400); + else + Output.Volume.StopRamp(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_LOCAL_14408.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_LOCAL_14408.cs new file mode 100644 index 00000000..4b4a927e --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_LOCAL_14408.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DM; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + public class DmCardAudioOutputController : IBasicVolumeWithFeedback + { + public Audio.Output Output { get; private set; } + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public BoolFeedback MuteFeedback { get; private set; } + + ushort PreMuteVolumeLevel; + bool IsMuted; + + public DmCardAudioOutputController(Audio.Output output) + { + Output = output; + VolumeLevelFeedback = new IntFeedback(() => Output.VolumeFeedback.UShortValue); + MuteFeedback = new BoolFeedback(() => IsMuted); + } + + #region IBasicVolumeWithFeedback Members + + /// + /// + /// + public void MuteOff() + { + SetVolume(PreMuteVolumeLevel); + IsMuted = false; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void MuteOn() + { + PreMuteVolumeLevel = Output.VolumeFeedback.UShortValue; + SetVolume(0); + IsMuted = true; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void SetVolume(ushort level) + { + Debug.Console(2, "Set volume out {0}", level); + Output.Volume.UShortValue = level; + } + + /// + /// + /// + internal void VolumeEventFromChassis() + { + VolumeLevelFeedback.FireUpdate(); + } + + #endregion + + #region IBasicVolumeControls Members + + /// + /// + /// + public void MuteToggle() + { + if (IsMuted) + MuteOff(); + else + MuteOn(); + } + + /// + /// + /// + public void VolumeDown(bool pressRelease) + { + if (pressRelease) + { + var remainingRatio = Output.Volume.UShortValue / 65535; + Output.Volume.CreateRamp(0, (uint)(400 * remainingRatio)); + } + else + Output.Volume.StopRamp(); + } + + /// + /// + /// + public void VolumeUp(bool pressRelease) + { + if (pressRelease) + { + var remainingRatio = (65535 - Output.Volume.UShortValue) / 65535; + Output.Volume.CreateRamp(65535, 400); + } + else + Output.Volume.StopRamp(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_REMOTE_14408.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_REMOTE_14408.cs new file mode 100644 index 00000000..6c5c0275 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmCardAudioOutput_REMOTE_14408.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DM; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + public class DmCardAudioOutputController : IBasicVolumeWithFeedback + { + public Audio.Output Output { get; private set; } + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public BoolFeedback MuteFeedback { get; private set; } + + ushort PreMuteVolumeLevel; + bool IsMuted; + + public DmCardAudioOutputController(Audio.Output output) + { + Output = output; + VolumeLevelFeedback = new IntFeedback(() => Output.VolumeFeedback.UShortValue); + MuteFeedback = new BoolFeedback(() => IsMuted); + } + + #region IBasicVolumeWithFeedback Members + + /// + /// + /// + public void MuteOff() + { + SetVolume(PreMuteVolumeLevel); + IsMuted = false; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void MuteOn() + { + PreMuteVolumeLevel = Output.VolumeFeedback.UShortValue; + SetVolume(0); + IsMuted = true; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public void SetVolume(ushort level) + { + Debug.Console(2, "Set volume out {0}", level); + Output.Volume.UShortValue = level; + } + + /// + /// + /// + internal void VolumeEventFromChassis() + { + VolumeLevelFeedback.FireUpdate(); + } + + #endregion + + #region IBasicVolumeControls Members + + /// + /// + /// + public void MuteToggle() + { + if (IsMuted) + MuteOff(); + else + MuteOn(); + } + + /// + /// + /// + public void VolumeDown(bool pressRelease) + { + if (pressRelease) + Output.Volume.CreateRamp(0, 400); + else + Output.Volume.StopRamp(); + } + + /// + /// + /// + public void VolumeUp(bool pressRelease) + { + if (pressRelease) + Output.Volume.CreateRamp(65535, 400); + else + Output.Volume.StopRamp(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs new file mode 100644 index 00000000..4e413749 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -0,0 +1,808 @@ + using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Cards; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.DM.Cards; + +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmChassisController : CrestronGenericBaseDevice, IRoutingInputsOutputs, IRouting, IHasFeedback//, ICardPortsDevice + { + public DmMDMnxn Chassis { get; private set; } + + // Feedbacks for EssentialDM + public Dictionary VideoOutputFeedbacks { get; private set; } + public Dictionary AudioOutputFeedbacks { get; private set; } + public Dictionary VideoInputSyncFeedbacks { get; private set; } + public Dictionary InputEndpointOnlineFeedbacks { get; private set; } + public Dictionary OutputEndpointOnlineFeedbacks { get; private set; } + public Dictionary InputNameFeedbacks { get; private set; } + public Dictionary OutputNameFeedbacks { get; private set; } + public Dictionary OutputVideoRouteNameFeedbacks { get; private set; } + public Dictionary OutputAudioRouteNameFeedbacks { get; private set; } + + + // Need a couple Lists of generic Backplane ports + public RoutingPortCollection InputPorts { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + public Dictionary TxDictionary { get; set; } + public Dictionary RxDictionary { get; set; } + + //public Dictionary InputCards { get; private set; } + //public Dictionary OutputCards { get; private set; } + + public Dictionary InputNames { get; set; } + public Dictionary OutputNames { get; set; } + public Dictionary VolumeControls { get; private set; } + + public const int RouteOffTime = 500; + Dictionary RouteOffTimers = new Dictionary(); + + /// + /// Factory method to create a new chassis controller from config data. Limited to 8x8 right now + /// + public static DmChassisController GetDmChassisController(string key, string name, + string type, DMChassisPropertiesConfig properties) + { + try + { + type = type.ToLower(); + uint ipid = properties.Control.IpIdInt; + + DmMDMnxn chassis = null; + if (type == "dmmd8x8") { chassis = new DmMd8x8(ipid, Global.ControlSystem); } + else if (type == "dmmd8x8rps") { chassis = new DmMd8x8rps(ipid, Global.ControlSystem); } + else if (type == "dmmd8x8cpu3") { chassis = new DmMd8x8Cpu3(ipid, Global.ControlSystem); } + else if (type == "dmmd8x8cpu3rps") { chassis = new DmMd8x8Cpu3rps(ipid, Global.ControlSystem); } + + else if (type == "dmmd16x16") { chassis = new DmMd16x16(ipid, Global.ControlSystem); } + else if (type == "dmmd16x16rps") { chassis = new DmMd16x16rps(ipid, Global.ControlSystem); } + else if (type == "dmmd16x16cpu3") { chassis = new DmMd16x16Cpu3(ipid, Global.ControlSystem); } + else if (type == "dmmd16x16cpu3rps") { chassis = new DmMd16x16Cpu3rps(ipid, Global.ControlSystem); } + + else if (type == "dmmd32x32") { chassis = new DmMd32x32(ipid, Global.ControlSystem); } + else if (type == "dmmd32x32rps") { chassis = new DmMd32x32rps(ipid, Global.ControlSystem); } + else if (type == "dmmd32x32cpu3") { chassis = new DmMd32x32Cpu3(ipid, Global.ControlSystem); } + else if (type == "dmmd32x32cpu3rps") { chassis = new DmMd32x32Cpu3rps(ipid, Global.ControlSystem); } + + if (chassis == null) + { + return null; + } + + var controller = new DmChassisController(key, name, chassis); + // add the cards and port names + foreach (var kvp in properties.InputSlots) + controller.AddInputCard(kvp.Value, kvp.Key); + foreach (var kvp in properties.OutputSlots) + { + controller.AddOutputCard(kvp.Value, kvp.Key); + } + + foreach (var kvp in properties.VolumeControls) + { + // get the card + // check it for an audio-compatible type + // make a something-something that will make it work + // retire to mountain village + var outNum = kvp.Key; + var card = controller.Chassis.Outputs[outNum].Card; + Audio.Output audio = null; + if (card is DmcHdo) + audio = (card as DmcHdo).Audio; + else if (card is Dmc4kHdo) + audio = (card as Dmc4kHdo).Audio; + if (audio == null) + continue; + // wire up the audio to something here... + controller.AddVolumeControl(outNum, audio); + } + + controller.InputNames = properties.InputNames; + controller.OutputNames = properties.OutputNames; + return controller; + + + //DmChassisController controller = null; + + //if (type == "dmmd8x8") + //{ + // controller = new DmChassisController(key, name, new DmMd8x8(ipid, Global.ControlSystem)); + + // // add the cards and port names + // foreach (var kvp in properties.InputSlots) + // controller.AddInputCard(kvp.Value, kvp.Key); + // foreach (var kvp in properties.OutputSlots) + // { + // controller.AddOutputCard(kvp.Value, kvp.Key); + + // } + + // foreach (var kvp in properties.VolumeControls) + // { + // // get the card + // // check it for an audio-compatible type + // // make a something-something that will make it work + // // retire to mountain village + // var outNum = kvp.Key; + // var card = controller.Chassis.Outputs[outNum].Card; + // Audio.Output audio = null; + // if (card is DmcHdo) + // audio = (card as DmcHdo).Audio; + // else if (card is Dmc4kHdo) + // audio = (card as Dmc4kHdo).Audio; + // if (audio == null) + // continue; + // // wire up the audio to something here... + // controller.AddVolumeControl(outNum, audio); + // } + + // controller.InputNames = properties.InputNames; + // controller.OutputNames = properties.OutputNames; + // return controller; + //} + //else if (type == "dmmd16x16") + //{ + // controller = new DmChassisController(key, name, new DmMd16x16(ipid, Global.ControlSystem)); + + // // add the cards and port names + // foreach (var kvp in properties.InputSlots) + // controller.AddInputCard(kvp.Value, kvp.Key); + // foreach (var kvp in properties.OutputSlots) + // { + // controller.AddOutputCard(kvp.Value, kvp.Key); + + // } + + // foreach (var kvp in properties.VolumeControls) + // { + // // get the card + // // check it for an audio-compatible type + // // make a something-something that will make it work + // // retire to mountain village + // var outNum = kvp.Key; + // var card = controller.Chassis.Outputs[outNum].Card; + // Audio.Output audio = null; + // if (card is DmcHdo) + // audio = (card as DmcHdo).Audio; + // else if (card is Dmc4kHdo) + // audio = (card as Dmc4kHdo).Audio; + // if (audio == null) + // continue; + // // wire up the audio to something here... + // controller.AddVolumeControl(outNum, audio); + // } + + // controller.InputNames = properties.InputNames; + // controller.OutputNames = properties.OutputNames; + // return controller; + //} + } + catch (System.Exception e) + { + Debug.Console(0, "Error creating DM chassis:\r{0}", e); + } + return null; + } + + + /// + /// + /// + /// + /// + /// + public DmChassisController(string key, string name, DmMDMnxn chassis) + : base(key, name, chassis) + { + Chassis = chassis; + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); + VolumeControls = new Dictionary(); + TxDictionary = new Dictionary(); + RxDictionary = new Dictionary(); + IsOnline.OutputChange += new EventHandler(IsOnline_OutputChange); + //IsOnline.OutputChange += new EventHandler(this.IsOnline_OutputChange); + Chassis.DMInputChange += new DMInputEventHandler(Chassis_DMInputChange); + //Chassis.DMSystemChange += new DMSystemEventHandler(Chassis_DMSystemChange); + Chassis.DMOutputChange += new DMOutputEventHandler(Chassis_DMOutputChange); + VideoOutputFeedbacks = new Dictionary(); + AudioOutputFeedbacks = new Dictionary(); + VideoInputSyncFeedbacks = new Dictionary(); + InputNameFeedbacks = new Dictionary(); + OutputNameFeedbacks = new Dictionary(); + OutputVideoRouteNameFeedbacks = new Dictionary(); + OutputAudioRouteNameFeedbacks = new Dictionary(); + InputEndpointOnlineFeedbacks = new Dictionary(); + OutputEndpointOnlineFeedbacks = new Dictionary(); + + + for (uint x = 1; x <= Chassis.NumberOfOutputs; x++) + { + var tempX = x; + + VideoOutputFeedbacks[tempX] = new IntFeedback(() => { + if (Chassis.Outputs[tempX].VideoOutFeedback != null) { return (ushort)Chassis.Outputs[tempX].VideoOutFeedback.Number;} + else { return 0; }; + }); + AudioOutputFeedbacks[tempX] = new IntFeedback(() => { + if (Chassis.Outputs[tempX].AudioOutFeedback != null) { return (ushort)Chassis.Outputs[tempX].AudioOutFeedback.Number; } + else { return 0; }; + }); + VideoInputSyncFeedbacks[tempX] = new BoolFeedback(() => { + return Chassis.Inputs[tempX].VideoDetectedFeedback.BoolValue; + }); + InputNameFeedbacks[tempX] = new StringFeedback(() => { + if (Chassis.Inputs[tempX].NameFeedback.StringValue != null) + { + return Chassis.Inputs[tempX].NameFeedback.StringValue; + } + else + { + return ""; + } + }); + OutputNameFeedbacks[tempX] = new StringFeedback(() => { + if (Chassis.Outputs[tempX].NameFeedback.StringValue != null) + { + return Chassis.Outputs[tempX].NameFeedback.StringValue; + } + else + { + return ""; + } + }); + OutputVideoRouteNameFeedbacks[tempX] = new StringFeedback(() => + { + if (Chassis.Outputs[tempX].VideoOutFeedback != null) + { + return Chassis.Outputs[tempX].VideoOutFeedback.NameFeedback.StringValue; + } + else + { + return ""; + } + }); + OutputAudioRouteNameFeedbacks[tempX] = new StringFeedback(() => + { + if (Chassis.Outputs[tempX].AudioOutFeedback != null) + { + return Chassis.Outputs[tempX].AudioOutFeedback.NameFeedback.StringValue; + } + else + { + return ""; + + } + }); + InputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => { return Chassis.Inputs[tempX].EndpointOnlineFeedback; }); + + OutputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => { return Chassis.Outputs[tempX].EndpointOnlineFeedback; }); + } + } + + /// + /// + /// + /// + /// + public void AddInputCard(string type, uint number) + { + Debug.Console(2, this, "Adding input card '{0}', slot {1}", type, number); + + if (type == "dmcHd") + { + var inputCard = new DmcHd(number, this.Chassis); + var cecPort = inputCard.HdmiInput as ICec; + AddHdmiInCardPorts(number, cecPort); + } + else if (type == "dmcHdDsp") + { + var inputCard = new DmcHdDsp(number, this.Chassis); + var cecPort = inputCard.HdmiInput as ICec; + AddHdmiInCardPorts(number, cecPort); + } + else if (type == "dmc4kHd") + { + var inputCard = new Dmc4kHd(number, this.Chassis); + var cecPort = inputCard.HdmiInput as ICec; + AddHdmiInCardPorts(number, cecPort); + } + else if (type == "dmc4kHdDsp") + { + var inputCard = new Dmc4kHdDsp(number, this.Chassis); + var cecPort = inputCard.HdmiInput as ICec; + AddHdmiInCardPorts(number, cecPort); + } + else if (type == "dmc4kzHd") + { + var inputCard = new Dmc4kzHd(number, this.Chassis); + var cecPort = inputCard.HdmiInput as ICec; + AddHdmiInCardPorts(number, cecPort); + } + else if (type == "dmc4kzHdDsp") + { + var inputCard = new Dmc4kzHdDsp(number, this.Chassis); + var cecPort = inputCard.HdmiInput as ICec; + AddHdmiInCardPorts(number, cecPort); + } + else if (type == "dmcC") + { + new DmcC(number, this.Chassis); + AddDmInCardPorts(number); + } + else if (type == "dmcCDsp") + { + new DmcCDsp(number, this.Chassis); + AddDmInCardPorts(number); + } + else if (type == "dmc4kC") + { + new Dmc4kC(number, this.Chassis); + AddDmInCardPorts(number); + } + else if (type == "dmc4kCDsp") + { + new Dmc4kCDsp(number, this.Chassis); + AddDmInCardPorts(number); + } + else if (type == "dmc4kzC") + { + new Dmc4kzC(number, this.Chassis); + AddDmInCardPorts(number); + } + else if (type == "dmc4kzCDsp") + { + new Dmc4kzCDsp(number, this.Chassis); + AddDmInCardPorts(number); + } + else if (type == "dmcCat") + { + new DmcCat(number, this.Chassis); + AddDmInCardPorts(number); + } + else if (type == "dmcCatDsp") + { + new DmcCatDsp(number, this.Chassis); + AddDmInCardPorts(number); + } + else if (type == "dmcS") + { + new DmcS(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmMmFiber); + AddInCardHdmiAndAudioLoopPorts(number); + } + else if (type == "dmcSDsp") + { + new DmcSDsp(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmMmFiber); + AddInCardHdmiAndAudioLoopPorts(number); + } + else if (type == "dmcS2") + { + new DmcS2(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmSmFiber); + AddInCardHdmiAndAudioLoopPorts(number); + } + else if (type == "dmcS2Dsp") + { + new DmcS2Dsp(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmSmFiber); + AddInCardHdmiAndAudioLoopPorts(number); + } + else if (type == "dmcSdi") + { + new DmcSdi(number, Chassis); + AddInputPortWithDebug(number, "sdiIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Sdi); + AddOutputPortWithDebug(number, "sdiOut", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Sdi, null); + AddInCardHdmiAndAudioLoopPorts(number); + } + else if (type == "dmcDvi") + { + new DmcDvi(number, Chassis); + AddInputPortWithDebug(number, "dviIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Dvi); + AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); + AddInCardHdmiLoopPort(number); + } + else if (type == "dmcVga") + { + new DmcVga(number, Chassis); + AddInputPortWithDebug(number, "vgaIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Vga); + AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); + AddInCardHdmiLoopPort(number); + } + else if (type == "dmcVidBnc") + { + new DmcVidBnc(number, Chassis); + AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); + AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); + AddInCardHdmiLoopPort(number); + } + else if (type == "dmcVidRcaA") + { + new DmcVidRcaA(number, Chassis); + AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); + AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); + AddInCardHdmiLoopPort(number); + } + else if (type == "dmcVidRcaD") + { + new DmcVidRcaD(number, Chassis); + AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); + AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.DigitalAudio); + AddInCardHdmiLoopPort(number); + } + else if (type == "dmcVid4") + { + new DmcVid4(number, Chassis); + AddInputPortWithDebug(number, "compositeIn1", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); + AddInputPortWithDebug(number, "compositeIn2", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); + AddInputPortWithDebug(number, "compositeIn3", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); + AddInputPortWithDebug(number, "compositeIn4", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); + AddInCardHdmiLoopPort(number); + } + else if (type == "dmcStr") + { + new DmcStr(number, Chassis); + AddInputPortWithDebug(number, "streamIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Streaming); + AddInCardHdmiAndAudioLoopPorts(number); + } + } + + void AddDmInCardPorts(uint number) + { + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmCat); + AddInCardHdmiAndAudioLoopPorts(number); + } + + void AddHdmiInCardPorts(uint number, ICec cecPort) + { + AddInputPortWithDebug(number, "hdmiIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, cecPort); + AddInCardHdmiAndAudioLoopPorts(number); + } + + void AddInCardHdmiAndAudioLoopPorts(uint number) + { + AddOutputPortWithDebug(number, "hdmiLoopOut", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null); + AddOutputPortWithDebug(number, "audioLoopOut", eRoutingSignalType.Audio, eRoutingPortConnectionType.Hdmi, null); + } + + void AddInCardHdmiLoopPort(uint number) + { + AddOutputPortWithDebug(number, "hdmiLoopOut", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null); + } + + /// + /// + /// + /// + /// + public void AddOutputCard(string type, uint number) + { + Debug.Console(2, this, "Adding output card '{0}', slot {1}", type, number); + if (type == "dmc4kHdo") + { + var outputCard = new Dmc4kHdoSingle(number, Chassis); + var cecPort1 = outputCard.Card1.HdmiOutput; + var cecPort2 = outputCard.Card2.HdmiOutput; + AddDmcHdoPorts(number, cecPort1, cecPort2); + } + else if (type == "dmcHdo") + { + var outputCard = new DmcHdoSingle(number, Chassis); + var cecPort1 = outputCard.Card1.HdmiOutput; + var cecPort2 = outputCard.Card2.HdmiOutput; + AddDmcHdoPorts(number, cecPort1, cecPort2); + } + else if (type == "dmc4kCoHd") + { + var outputCard = new Dmc4kCoHdSingle(number, Chassis); + var cecPort1 = outputCard.Card1.HdmiOutput; + AddDmcCoPorts(number, cecPort1); + } + else if (type == "dmc4kzCoHd") + { + var outputCard = new Dmc4kzCoHdSingle(number, Chassis); + var cecPort1 = outputCard.Card1.HdmiOutput; + AddDmcCoPorts(number, cecPort1); + } + else if (type == "dmcCoHd") + { + var outputCard = new DmcCoHdSingle(number, Chassis); + var cecPort1 = outputCard.Card1.HdmiOutput; + AddDmcCoPorts(number, cecPort1); + } + else if (type == "dmCatoHd") + { + var outputCard = new DmcCatoHdSingle(number, Chassis); + var cecPort1 = outputCard.Card1.HdmiOutput; + AddDmcCoPorts(number, cecPort1); + } + else if (type == "dmcSoHd") + { + var outputCard = new DmcSoHdSingle(number, Chassis); + var cecPort1 = outputCard.Card1.HdmiOutput; + AddOutputPortWithDebug(number, "dmOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmMmFiber, 2 * (number - 1) + 1); + AddOutputPortWithDebug(number, "hdmiOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2 * (number - 1) + 1, cecPort1); + AddOutputPortWithDebug(number, "dmOut2", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmMmFiber, 2 * (number - 1) + 2); + + } + else if (type == "dmcS2oHd") + { + var outputCard = new DmcS2oHdSingle(number, Chassis); + var cecPort1 = outputCard.Card1.HdmiOutput; + AddOutputPortWithDebug(number, "dmOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmSmFiber, 2 * (number - 1) + 1); + AddOutputPortWithDebug(number, "hdmiOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2 * (number - 1) + 1, cecPort1); + AddOutputPortWithDebug(number, "dmOut2", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmSmFiber, 2 * (number - 1) + 2); + } + else if (type == "dmcStro") + { + var outputCard = new DmcStroSingle(number, Chassis); + AddOutputPortWithDebug(number, "streamOut", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Streaming, 2 * (number - 1) + 1); + } + + else + Debug.Console(1, this, " WARNING: Output card type '{0}' is not available", type); + } + + void AddDmcHdoPorts(uint number, ICec cecPort1, ICec cecPort2) + { + AddOutputPortWithDebug(number, "hdmiOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2 * (number - 1) + 1, cecPort1); + AddOutputPortWithDebug(number, "audioOut1", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio, 2 * (number - 1) + 1); + AddOutputPortWithDebug(number, "hdmiOut2", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2 * (number - 1) + 2, cecPort2); + AddOutputPortWithDebug(number, "audioOut2", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio, 2 * (number - 1) + 2); + } + + void AddDmcCoPorts(uint number, ICec cecPort1) + { + AddOutputPortWithDebug(number, "dmOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmCat, 2 * (number - 1) + 1); + AddOutputPortWithDebug(number, "hdmiOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2 * (number - 1) + 1, cecPort1); + AddOutputPortWithDebug(number, "dmOut2", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmCat, 2 * (number - 1) + 2); + } + + + /// + /// Adds InputPort + /// + void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType) + { + var portKey = string.Format("inputCard{0}--{1}", cardNum, portName); + Debug.Console(2, this, "Adding input port '{0}'", portKey); + var inputPort = new RoutingInputPort(portKey, sigType, portType, cardNum, this); + + InputPorts.Add(inputPort); + } + + /// + /// Adds InputPort and sets Port as ICec object + /// + void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, ICec cecPort) + { + var portKey = string.Format("inputCard{0}--{1}", cardNum, portName); + Debug.Console(2, this, "Adding input port '{0}'", portKey); + var inputPort = new RoutingInputPort(portKey, sigType, portType, cardNum, this); + + if (cecPort != null) + inputPort.Port = cecPort; + + InputPorts.Add(inputPort); + } + + /// + /// Adds OutputPort + /// + void AddOutputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector) + { + var portKey = string.Format("outputCard{0}--{1}", cardNum, portName); + Debug.Console(2, this, "Adding output port '{0}'", portKey); + OutputPorts.Add(new RoutingOutputPort(portKey, sigType, portType, selector, this)); + } + + /// + /// Adds OutputPort and sets Port as ICec object + /// + void AddOutputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector, ICec cecPort) + { + var portKey = string.Format("outputCard{0}--{1}", cardNum, portName); + Debug.Console(2, this, "Adding output port '{0}'", portKey); + var outputPort = new RoutingOutputPort(portKey, sigType, portType, selector, this); + + if (cecPort != null) + outputPort.Port = cecPort; + + OutputPorts.Add(outputPort); + } + + /// + /// + /// + void AddVolumeControl(uint number, Audio.Output audio) + { + VolumeControls.Add(number, new DmCardAudioOutputController(audio)); + } + + //public void SetInputHdcpSupport(uint input, ePdtHdcpSupport hdcpSetting) + //{ + + //} + + + void Chassis_DMSystemChange(Switch device, DMSystemEventArgs args) { + + } + void Chassis_DMInputChange(Switch device, DMInputEventArgs args) { + //Debug.Console(2, this, "DMSwitch:{0} Input:{1} Event:{2}'", this.Name, args.Number, args.EventId.ToString()); + + switch (args.EventId) { + case (DMInputEventIds.OnlineFeedbackEventId): { + Debug.Console(2, this, "DMINput OnlineFeedbackEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback); + InputEndpointOnlineFeedbacks[args.Number].FireUpdate(); + break; + } + case (DMInputEventIds.VideoDetectedEventId): { + Debug.Console(2, this, "DM Input {0} VideoDetectedEventId", args.Number); + VideoInputSyncFeedbacks[args.Number].FireUpdate(); + break; + } + case (DMInputEventIds.InputNameEventId): { + Debug.Console(2, this, "DM Input {0} NameFeedbackEventId", args.Number); + InputNameFeedbacks[args.Number].FireUpdate(); + break; + } + } + } + /// + /// + void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) + { + + //This should be a switch case JTA 2018-07-02 + var output = args.Number; + if (args.EventId == DMOutputEventIds.VolumeEventId && + VolumeControls.ContainsKey(output)) + { + VolumeControls[args.Number].VolumeEventFromChassis(); + } + else if (args.EventId == DMOutputEventIds.OnlineFeedbackEventId) + { + OutputEndpointOnlineFeedbacks[output].FireUpdate(); + } + else if (args.EventId == DMOutputEventIds.VideoOutEventId) + { + if (Chassis.Outputs[output].VideoOutFeedback != null) + { + Debug.Console(2, this, "DMSwitchVideo:{0} Routed Input:{1} Output:{2}'", this.Name, Chassis.Outputs[output].VideoOutFeedback.Number, output); + } + if (VideoOutputFeedbacks.ContainsKey(output)) + { + VideoOutputFeedbacks[output].FireUpdate(); + + } + if (OutputVideoRouteNameFeedbacks.ContainsKey(output)) + { + OutputVideoRouteNameFeedbacks[output].FireUpdate(); + } + } + else if (args.EventId == DMOutputEventIds.AudioOutEventId) + { + if (Chassis.Outputs[output].AudioOutFeedback != null) + { + Debug.Console(2, this, "DMSwitchAudio:{0} Routed Input:{1} Output:{2}'", this.Name, Chassis.Outputs[output].AudioOutFeedback.Number, output); + } + if (AudioOutputFeedbacks.ContainsKey(output)) + { + AudioOutputFeedbacks[output].FireUpdate(); + } + } + else if (args.EventId == DMOutputEventIds.OutputNameEventId) + { + Debug.Console(2, this, "DM Output {0} NameFeedbackEventId", output); + OutputNameFeedbacks[output].FireUpdate(); + } + + } + + /// + /// + /// + /// + void StartOffTimer(PortNumberType pnt) + { + if (RouteOffTimers.ContainsKey(pnt)) + return; + RouteOffTimers[pnt] = new CTimer(o => + { + ExecuteSwitch(0, pnt.Number, pnt.Type); + }, RouteOffTime); + } + + + // Send out sigs when coming online + void IsOnline_OutputChange(object sender, EventArgs e) + { + if (IsOnline.BoolValue) + { + Chassis.EnableAudioBreakaway.BoolValue = true; + //Chassis.EnableUSBBreakaway.BoolValue = true; + + if (InputNames != null) + foreach (var kvp in InputNames) + Chassis.Inputs[kvp.Key].Name.StringValue = kvp.Value; + if (OutputNames != null) + foreach(var kvp in OutputNames) + Chassis.Outputs[kvp.Key].Name.StringValue = kvp.Value; + } + } + + #region IRouting Members + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType sigType) + { + Debug.Console(2, this, "Making an awesome DM route from {0} to {1} {2}", inputSelector, outputSelector, sigType); + + var input = Convert.ToUInt32(inputSelector); // Cast can sometimes fail + var output = Convert.ToUInt32(outputSelector); + // Check to see if there's an off timer waiting on this and if so, cancel + var key = new PortNumberType(output, sigType); + if (input == 0) + { + StartOffTimer(key); + } + else + { + if(RouteOffTimers.ContainsKey(key)) + { + Debug.Console(2, this, "{0} cancelling route off due to new source", output); + RouteOffTimers[key].Stop(); + RouteOffTimers.Remove(key); + } + } + + Card.DMICard inCard = input == 0 ? null : Chassis.Inputs[input]; + + // NOTE THAT THESE ARE NOTS - TO CATCH THE AudioVideo TYPE + if (sigType != eRoutingSignalType.Audio) + { + Chassis.VideoEnter.BoolValue = true; + Chassis.Outputs[output].VideoOut = inCard; + } + + if (sigType != eRoutingSignalType.Video) + { + Chassis.AudioEnter.BoolValue = true; + Chassis.Outputs[output].AudioOut = inCard; + } + } + + #endregion + + } + + public struct PortNumberType + { + public uint Number { get; private set; } + public eRoutingSignalType Type { get; private set; } + + public PortNumberType(uint number, eRoutingSignalType type) : this() + { + Number = number; + Type = type; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs new file mode 100644 index 00000000..e95cd5bd --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DM; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM.Chassis +{ + public class HdMdNxM4kEController : Device, IRoutingInputsOutputs, IRouting + { + public HdMdNxM Chassis { get; private set; } + + public RoutingPortCollection InputPorts { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + + /// + /// + /// + /// + /// + /// + public HdMdNxM4kEController(string key, string name, HdMdNxM chassis, + HdMdNxM4kEPropertiesConfig props) + : base(key, name) + { + Chassis = chassis; + + // logical ports + InputPorts = new RoutingPortCollection(); + for (uint i = 1; i <= 4; i++) + { + InputPorts.Add(new RoutingInputPort("hdmiIn" + i, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, i, this)); + } + OutputPorts = new RoutingPortCollection(); + OutputPorts.Add(new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this)); + + // physical settings + if (props != null && props.Inputs != null) + { + foreach (var kvp in props.Inputs) + { + // strip "hdmiIn" + var inputNum = Convert.ToUInt32(kvp.Key.Substring(6)); + + var port = chassis.HdmiInputs[inputNum].HdmiInputPort; + // set hdcp disables + if (kvp.Value.DisableHdcp) + { + Debug.Console(0, this, "Configuration disables HDCP support on {0}", kvp.Key); + port.HdcpSupportOff(); + } + else + port.HdcpSupportOn(); + } + } + } + + public override bool CustomActivate() + { + var result = Chassis.Register(); + if (result != Crestron.SimplSharpPro.eDeviceRegistrationUnRegistrationResponse.Success) + { + Debug.Console(0, this, "Device registration failed: {0}", result); + return false; + } + + return base.CustomActivate(); + } + + + + #region IRouting Members + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + // Try to make switch only when necessary. The unit appears to toggle when already selected. + var current = Chassis.HdmiOutputs[1].VideoOut; + if(current != Chassis.HdmiInputs[(uint)inputSelector]) + Chassis.HdmiOutputs[1].VideoOut = Chassis.HdmiInputs[(uint)inputSelector]; + } + + #endregion + + ///////////////////////////////////////////////////// + + /// + /// + /// + /// + /// + /// + /// + /// + public static HdMdNxM4kEController GetController(string key, string name, + string type, HdMdNxM4kEPropertiesConfig properties) + { + try + { + var ipid = properties.Control.IpIdInt; + var address = properties.Control.TcpSshProperties.Address; + + type = type.ToLower(); + if (type == "hdmd4x14ke") + { + var chassis = new HdMd4x14kE(ipid, address, Global.ControlSystem); + return new HdMdNxM4kEController(key, name, chassis, properties); + } + return null; + } + catch (Exception e) + { + Debug.Console(0, "ERROR Creating device key {0}: \r{1}", key, e); + return null; + } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Config/DMChassisConfig.cs b/essentials-framework/Essentials DM/Essentials_DM/Config/DMChassisConfig.cs new file mode 100644 index 00000000..81f28afa --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Config/DMChassisConfig.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharpPro.DM; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.DM.Cards; + +namespace PepperDash.Essentials.DM.Config +{ + /// + /// Represents the "properties" property of a DM device config + /// + public class DMChassisPropertiesConfig + { + [JsonProperty("control")] + public ControlPropertiesConfig Control { get; set; } + + [JsonProperty("volumeControls")] + public Dictionary VolumeControls { get; set; } + + [JsonProperty("inputSlots")] + public Dictionary InputSlots { get; set; } + + [JsonProperty("outputSlots")] + public Dictionary OutputSlots { get; set; } + + [JsonProperty("inputNames")] + public Dictionary InputNames { get; set; } + + [JsonProperty("outputNames")] + public Dictionary OutputNames { get; set; } + } + + /// + /// + /// + public class DmCardAudioPropertiesConfig + { + [JsonProperty("outLevel")] + public int OutLevel { get; set; } + + [JsonProperty("isVolumeControlPoint")] + public bool IsVolumeControlPoint { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Config/DeviceFactory.cs b/essentials-framework/Essentials DM/Essentials_DM/Config/DeviceFactory.cs new file mode 100644 index 00000000..f425843f --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Config/DeviceFactory.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM.AirMedia; +using Crestron.SimplSharpPro.UI; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.DM.AirMedia; +using PepperDash.Essentials.DM.Endpoints.DGEs; + +namespace PepperDash.Essentials.DM +{ + public class DeviceFactory + { + public static IKeyed GetDevice(DeviceConfig dc) + { + var key = dc.Key; + var name = dc.Name; + var type = dc.Type; + var properties = dc.Properties; + + var typeName = dc.Type.ToLower(); + + if (typeName.StartsWith("am")) + { + var props = JsonConvert.DeserializeObject(properties.ToString()); + AmX00 amDevice = null; + if (typeName == "am200") + amDevice = new Crestron.SimplSharpPro.DM.AirMedia.Am200(props.Control.IpIdInt, Global.ControlSystem); + else if(typeName == "am300") + amDevice = new Crestron.SimplSharpPro.DM.AirMedia.Am300(props.Control.IpIdInt, Global.ControlSystem); + + return new AirMediaController(key, name, amDevice, dc, props); + } + else if (typeName.StartsWith("dmmd8x") || typeName.StartsWith("dmmd16x") || typeName.StartsWith("dmmd32x")) + { + var props = JsonConvert.DeserializeObject + (properties.ToString()); + return PepperDash.Essentials.DM.DmChassisController. + GetDmChassisController(key, name, type, props); + } + // Hand off to DmTxHelper class + else if (typeName.StartsWith("dmtx")) + { + var props = JsonConvert.DeserializeObject + (properties.ToString()); + return PepperDash.Essentials.DM.DmTxHelper.GetDmTxController(key, name, type, props); + } + + // Hand off to DmRmcHelper class + else if (typeName.StartsWith("dmrmc")) + { + var props = JsonConvert.DeserializeObject + (properties.ToString()); + return PepperDash.Essentials.DM.DmRmcHelper.GetDmRmcController(key, name, type, props); + } + + else if (typeName.Equals("hdmd4x14ke")) + { + var props = JsonConvert.DeserializeObject + (properties.ToString()); + return PepperDash.Essentials.DM.Chassis.HdMdNxM4kEController.GetController(key, name, type, props); + } + + + + return null; + } + + + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Config/DmRmcConfig.cs b/essentials-framework/Essentials DM/Essentials_DM/Config/DmRmcConfig.cs new file mode 100644 index 00000000..702e10b5 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Config/DmRmcConfig.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharpPro.DM; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using PepperDash.Core; +using PepperDash.Essentials.DM.Cards; + +namespace PepperDash.Essentials.DM.Config +{ + /// + /// Represents the "properties" property of a DM TX device config + /// + public class DmRmcPropertiesConfig + { + [JsonProperty("control")] + public ControlPropertiesConfig Control { get; set; } + + [JsonProperty("parentDeviceKey")] + public string ParentDeviceKey { get; set; } + + [JsonProperty("parentOutputNumber")] + public uint ParentOutputNumber { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Config/DmTxConfig.cs b/essentials-framework/Essentials DM/Essentials_DM/Config/DmTxConfig.cs new file mode 100644 index 00000000..51d074f8 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Config/DmTxConfig.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharpPro.DM; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using PepperDash.Core; +using PepperDash.Essentials.DM.Cards; + +namespace PepperDash.Essentials.DM.Config +{ + /// + /// Represents the "properties" property of a DM TX device config + /// + public class DmTxPropertiesConfig + { + [JsonProperty("control")] + public ControlPropertiesConfig Control { get; set; } + + [JsonProperty("parentDeviceKey")] + public string ParentDeviceKey { get; set; } + + [JsonProperty("parentInputNumber")] + public uint ParentInputNumber { get; set; } + + [JsonProperty("autoSwitching")] + public bool AutoSwitching { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Config/HdMdNxM4kEPropertiesConfig.cs b/essentials-framework/Essentials DM/Essentials_DM/Config/HdMdNxM4kEPropertiesConfig.cs new file mode 100644 index 00000000..06411e39 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Config/HdMdNxM4kEPropertiesConfig.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; + +using PepperDash.Core; + +namespace PepperDash.Essentials.DM.Config +{ + /// + /// Defines the properties section of HdMdNxM boxes + /// + public class HdMdNxM4kEPropertiesConfig + { + [JsonProperty("control")] + public ControlPropertiesConfig Control { get; set; } + + [JsonProperty("inputs")] + public Dictionary Inputs { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Config/InputPropertiesConfig.cs b/essentials-framework/Essentials DM/Essentials_DM/Config/InputPropertiesConfig.cs new file mode 100644 index 00000000..40f958ec --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Config/InputPropertiesConfig.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.DM.Config +{ + public class InputPropertiesConfig + { + public string Name { get; set; } + + public bool DisableHdcp { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/DmPortName.cs b/essentials-framework/Essentials DM/Essentials_DM/DmPortName.cs new file mode 100644 index 00000000..c862f1ed --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/DmPortName.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.DM +{ + /// + /// Constants for consistent port naming + /// + public class DmPortName + { + public const string AirBoardIn = "AirBoardIn"; + public const string AirMediaIn = "AirMediaIn"; + public const string AnyVideoIn = "AnyVideoIn"; + public const string AudioLoopOut = "AudioLoopOut"; + public const string BalancedAudioOut = "BalancedAudioOut"; + public const string BalancedAudioOut1 = "BalancedAudioOut1"; + public const string BalancedAudioOut2 = "BalancedAudioOut2"; + public const string CompositeIn = "CompositeIn"; + public const string DisplayPortIn = "DisplayPortIn"; + public const string DmIn = "DmIn"; + public const string DmOut = "DmOut"; + public const string DmOut1 = "DmOut1"; + public const string DmOut2 = "DmOut2"; + public const string HdmiIn = "HdmiIn"; + public const string HdmiIn1 = "HdmiIn1"; + public const string HdmiIn2 = "HdmiIn2"; + public const string HdmiOut1 = "HdmiOut1"; + public const string HdmiOut2 = "HdmiOut2"; + public const string HdmiLoopOut = "HdmiLoopOut"; + public const string HdmiOut = "HdmiOut"; + public const string Osd = "Osd"; + public const string VgaIn = "VgaIn"; + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/DgeController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/DgeController.cs new file mode 100644 index 00000000..f8cf0938 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/DgeController.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +using Newtonsoft.Json; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.DM.Endpoints.DGEs +{ + /// + /// Wrapper class for DGE-100 and DM-DGE-200-C + /// + public class DgeController : CrestronGenericBaseDevice, IComPorts, IIROutputPorts + { + public Dge100 DigitalGraphicsEngine { get; private set; } + + public DeviceConfig DeviceConfig { get; private set; } + + CrestronTouchpanelPropertiesConfig PropertiesConfig; + + public DgeController(string key, string name, Dge100 device, DeviceConfig dc, CrestronTouchpanelPropertiesConfig props) + :base(key, name, device) + { + DeviceConfig = dc; + + PropertiesConfig = props; + } + + #region IComPorts Members + + public CrestronCollection ComPorts + { + get { return DigitalGraphicsEngine.ComPorts; } + } + + public int NumberOfComPorts + { + get { return DigitalGraphicsEngine.NumberOfComPorts; } + } + + #endregion + + #region IIROutputPorts Members + + public CrestronCollection IROutputPorts + { + get { return DigitalGraphicsEngine.IROutputPorts; } + } + + public int NumberOfIROutputPorts + { + get { return DigitalGraphicsEngine.NumberOfIROutputPorts; } + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/DgePropertiesConfig.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/DgePropertiesConfig.cs new file mode 100644 index 00000000..2b741efa --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/DgePropertiesConfig.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.DM.Endpoints.DGEs +{ + public class DgePropertiesConfig + { + [JsonProperty("control")] + public ControlPropertiesConfig Control { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/NVX/DmNvx35xController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/NVX/DmNvx35xController.cs new file mode 100644 index 00000000..cd12ea04 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/NVX/DmNvx35xController.cs @@ -0,0 +1,19 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; + +//using Crestron.SimplSharpPro.DM.Streaming; + +//using PepperDash.Core; +//using PepperDash.Essentials.Core; + +//namespace PepperDash.Essentials.DM.Endpoints.NVX +//{ +// public class DmNvx35xController: DmNvxControllerBase +// { + + +// } +//} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/NVX/DmNvxConfig.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/NVX/DmNvxConfig.cs new file mode 100644 index 00000000..b4cecc4e --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/NVX/DmNvxConfig.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DM.Streaming; + +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using PepperDash.Core; + +namespace PepperDash.Essentials.DM.Endpoints.NVX +{ + /// + /// Represents the "properties" property of a DM NVX device config + /// + public class DmNvxConfig + { + [JsonProperty("control")] + public ControlPropertiesConfig Control { get; set; } + + [JsonProperty("parrentDeviceKey")] + public string ParentDeviceKey { get; set; } + + [JsonProperty("deviceMode")] + public eDeviceMode DeviceMode { get; set; } + + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/NVX/DmNvxControllerBase.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/NVX/DmNvxControllerBase.cs new file mode 100644 index 00000000..6063b893 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/NVX/DmNvxControllerBase.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM.Streaming; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM.Endpoints.NVX +{ + public abstract class DmNvxControllerBase: CrestronGenericBaseDevice + { + public DmNvx35x DmNvx { get; private set; } + + + + public abstract StringFeedback ActiveVideoInputFeedback { get; protected set; } + public RoutingInputPortWithVideoStatuses AnyVideoInput { get; protected set; } + + + public DmNvxControllerBase(string key, string name, DmNvxBaseClass hardware) + : base(key, name, hardware) + { + AddToFeedbackList(ActiveVideoInputFeedback); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmHdBaseTEndpointController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmHdBaseTEndpointController.cs new file mode 100644 index 00000000..e269516f --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmHdBaseTEndpointController.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + public class HDBaseTRxController : DmHdBaseTControllerBase, IRoutingInputsOutputs, + IComPorts + { + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HDBaseTSink { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HDBaseTSink }; } + } + + public HDBaseTRxController(string key, string name, HDRx3CB rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HDBaseTSink = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + // Set Ports for CEC + HDBaseTSink.Port = Rmc; // Unique case, this class has no HdmiOutput port and ICec is implemented on the receiver class itself + } + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc100SController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc100SController.cs new file mode 100644 index 00000000..6019ee75 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc100SController.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmRmc100SController : DmRmcControllerBase, IRoutingInputsOutputs, + IIROutputPorts, IComPorts, ICec + { + public DmRmc100S Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmc100SController(string key, string name, DmRmc100S rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + // Set Ports for CEC + HdmiOut.Port = Rmc; // Unique case, this class has no HdmiOutput port and ICec is implemented on the receiver class itself + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + public Cec StreamCec { get { return Rmc.HdmiOutput.StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc150SController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc150SController.cs new file mode 100644 index 00000000..8deffb19 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc150SController.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmRmc150SController : DmRmcControllerBase, IRoutingInputsOutputs, + IIROutputPorts, IComPorts, ICec + { + public DmRmc150S Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmc150SController(string key, string name, DmRmc150S rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + EdidManufacturerFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Manufacturer.StringValue); + EdidNameFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Name.StringValue); + EdidPreferredTimingFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.PreferredTiming.StringValue); + EdidSerialNumberFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.SerialNumber.StringValue); + + //VideoOutputResolutionFeedback = new StringFeedback(() => Rmc.HdmiOutput.GetVideoResolutionString()); + + //Rmc.HdmiOutput.OutputStreamChange += HdmiOutput_OutputStreamChange; + Rmc.HdmiOutput.ConnectedDevice.DeviceInformationChange += ConnectedDevice_DeviceInformationChange; + + // Set Ports for CEC + HdmiOut.Port = Rmc.HdmiOutput; + } + + //void HdmiOutput_OutputStreamChange(EndpointOutputStream outputStream, EndpointOutputStreamEventArgs args) + //{ + // if (args.EventId == EndpointOutputStreamEventIds.HorizontalResolutionFeedbackEventId || args.EventId == EndpointOutputStreamEventIds.VerticalResolutionFeedbackEventId || + // args.EventId == EndpointOutputStreamEventIds.FramesPerSecondFeedbackEventId) + // { + // VideoOutputResolutionFeedback.FireUpdate(); + // } + //} + + void ConnectedDevice_DeviceInformationChange(ConnectedDeviceInformation connectedDevice, ConnectedDeviceEventArgs args) + { + if (args.EventId == ConnectedDeviceEventIds.ManufacturerEventId) + { + EdidManufacturerFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.NameEventId) + { + EdidNameFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.PreferredTimingEventId) + { + EdidPreferredTimingFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.SerialNumberEventId) + { + EdidSerialNumberFeedback.FireUpdate(); + } + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + public Cec StreamCec { get { return Rmc.HdmiOutput.StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc200CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc200CController.cs new file mode 100644 index 00000000..03d00149 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc200CController.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmRmc200CController : DmRmcControllerBase, IRoutingInputsOutputs, + IIROutputPorts, IComPorts, ICec + { + public DmRmc200C Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmc200CController(string key, string name, DmRmc200C rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + EdidManufacturerFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Manufacturer.StringValue); + EdidNameFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Name.StringValue); + EdidPreferredTimingFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.PreferredTiming.StringValue); + EdidSerialNumberFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.SerialNumber.StringValue); + + VideoOutputResolutionFeedback = new StringFeedback(() => Rmc.HdmiOutput.GetVideoResolutionString()); + + Rmc.HdmiOutput.OutputStreamChange += HdmiOutput_OutputStreamChange; + Rmc.HdmiOutput.ConnectedDevice.DeviceInformationChange += ConnectedDevice_DeviceInformationChange; + + // Set Ports for CEC + HdmiOut.Port = Rmc.HdmiOutput; + } + + void HdmiOutput_OutputStreamChange(EndpointOutputStream outputStream, EndpointOutputStreamEventArgs args) + { + if (args.EventId == EndpointOutputStreamEventIds.HorizontalResolutionFeedbackEventId || args.EventId == EndpointOutputStreamEventIds.VerticalResolutionFeedbackEventId || + args.EventId == EndpointOutputStreamEventIds.FramesPerSecondFeedbackEventId) + { + VideoOutputResolutionFeedback.FireUpdate(); + } + } + + void ConnectedDevice_DeviceInformationChange(ConnectedDeviceInformation connectedDevice, ConnectedDeviceEventArgs args) + { + if (args.EventId == ConnectedDeviceEventIds.ManufacturerEventId) + { + EdidManufacturerFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.NameEventId) + { + EdidNameFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.PreferredTimingEventId) + { + EdidPreferredTimingFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.SerialNumberEventId) + { + EdidSerialNumberFeedback.FireUpdate(); + } + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + public Cec StreamCec { get { return Rmc.HdmiOutput.StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc200S2Controller.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc200S2Controller.cs new file mode 100644 index 00000000..11bd5940 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc200S2Controller.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmRmc200S2Controller : DmRmcControllerBase, IRoutingInputsOutputs, + IIROutputPorts, IComPorts, ICec + { + public DmRmc200S2 Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmc200S2Controller(string key, string name, DmRmc200S2 rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + EdidManufacturerFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Manufacturer.StringValue); + EdidNameFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Name.StringValue); + EdidPreferredTimingFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.PreferredTiming.StringValue); + EdidSerialNumberFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.SerialNumber.StringValue); + + VideoOutputResolutionFeedback = new StringFeedback(() => Rmc.HdmiOutput.GetVideoResolutionString()); + + Rmc.HdmiOutput.OutputStreamChange += HdmiOutput_OutputStreamChange; + Rmc.HdmiOutput.ConnectedDevice.DeviceInformationChange += ConnectedDevice_DeviceInformationChange; + + // Set Ports for CEC + HdmiOut.Port = Rmc.HdmiOutput; + } + + void HdmiOutput_OutputStreamChange(EndpointOutputStream outputStream, EndpointOutputStreamEventArgs args) + { + if (args.EventId == EndpointOutputStreamEventIds.HorizontalResolutionFeedbackEventId || args.EventId == EndpointOutputStreamEventIds.VerticalResolutionFeedbackEventId || + args.EventId == EndpointOutputStreamEventIds.FramesPerSecondFeedbackEventId) + { + VideoOutputResolutionFeedback.FireUpdate(); + } + } + + void ConnectedDevice_DeviceInformationChange(ConnectedDeviceInformation connectedDevice, ConnectedDeviceEventArgs args) + { + if (args.EventId == ConnectedDeviceEventIds.ManufacturerEventId) + { + EdidManufacturerFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.NameEventId) + { + EdidNameFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.PreferredTimingEventId) + { + EdidPreferredTimingFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.SerialNumberEventId) + { + EdidSerialNumberFeedback.FireUpdate(); + } + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + public Cec StreamCec { get { return Rmc.HdmiOutput.StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc200SController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc200SController.cs new file mode 100644 index 00000000..ed172723 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc200SController.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmRmc200SController : DmRmcControllerBase, IRoutingInputsOutputs, + IIROutputPorts, IComPorts, ICec + { + public DmRmc200S Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmc200SController(string key, string name, DmRmc200S rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + EdidManufacturerFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Manufacturer.StringValue); + EdidNameFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Name.StringValue); + EdidPreferredTimingFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.PreferredTiming.StringValue); + EdidSerialNumberFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.SerialNumber.StringValue); + + VideoOutputResolutionFeedback = new StringFeedback(() => Rmc.HdmiOutput.GetVideoResolutionString()); + + Rmc.HdmiOutput.OutputStreamChange += HdmiOutput_OutputStreamChange; + Rmc.HdmiOutput.ConnectedDevice.DeviceInformationChange += ConnectedDevice_DeviceInformationChange; + + // Set Ports for CEC + HdmiOut.Port = Rmc.HdmiOutput; + } + + void HdmiOutput_OutputStreamChange(EndpointOutputStream outputStream, EndpointOutputStreamEventArgs args) + { + if (args.EventId == EndpointOutputStreamEventIds.HorizontalResolutionFeedbackEventId || args.EventId == EndpointOutputStreamEventIds.VerticalResolutionFeedbackEventId || + args.EventId == EndpointOutputStreamEventIds.FramesPerSecondFeedbackEventId) + { + VideoOutputResolutionFeedback.FireUpdate(); + } + } + + void ConnectedDevice_DeviceInformationChange(ConnectedDeviceInformation connectedDevice, ConnectedDeviceEventArgs args) + { + if (args.EventId == ConnectedDeviceEventIds.ManufacturerEventId) + { + EdidManufacturerFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.NameEventId) + { + EdidNameFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.PreferredTimingEventId) + { + EdidPreferredTimingFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.SerialNumberEventId) + { + EdidSerialNumberFeedback.FireUpdate(); + } + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + public Cec StreamCec { get { return Rmc.HdmiOutput.StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4KScalerCController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4KScalerCController.cs new file mode 100644 index 00000000..02cb53c0 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4KScalerCController.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmRmc4kScalerCController : DmRmcControllerBase, IRoutingInputsOutputs, IBasicVolumeWithFeedback, + IIROutputPorts, IComPorts, ICec, IRelayPorts + { + public DmRmc4kScalerC Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + public RoutingOutputPort BalancedAudioOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut, BalancedAudioOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmc4kScalerCController(string key, string name, DmRmc4kScalerC rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + BalancedAudioOut = new RoutingOutputPort(DmPortName.BalancedAudioOut, eRoutingSignalType.Audio, + eRoutingPortConnectionType.LineAudio, null, this); + + MuteFeedback = new BoolFeedback(() => false); + VolumeLevelFeedback = new IntFeedback("MainVolumeLevelFeedback", () => + rmc.AudioOutput.VolumeFeedback.UShortValue); + + EdidManufacturerFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Manufacturer.StringValue); + EdidNameFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Name.StringValue); + EdidPreferredTimingFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.PreferredTiming.StringValue); + EdidSerialNumberFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.SerialNumber.StringValue); + + VideoOutputResolutionFeedback = new StringFeedback(() => Rmc.HdmiOutput.GetVideoResolutionString()); + + Rmc.HdmiOutput.OutputStreamChange += HdmiOutput_OutputStreamChange; + Rmc.HdmiOutput.ConnectedDevice.DeviceInformationChange += ConnectedDevice_DeviceInformationChange; + + // Set Ports for CEC + HdmiOut.Port = Rmc.HdmiOutput; + } + + void HdmiOutput_OutputStreamChange(EndpointOutputStream outputStream, EndpointOutputStreamEventArgs args) + { + if (args.EventId == EndpointOutputStreamEventIds.HorizontalResolutionFeedbackEventId || args.EventId == EndpointOutputStreamEventIds.VerticalResolutionFeedbackEventId || + args.EventId == EndpointOutputStreamEventIds.FramesPerSecondFeedbackEventId) + { + VideoOutputResolutionFeedback.FireUpdate(); + } + } + + void ConnectedDevice_DeviceInformationChange(ConnectedDeviceInformation connectedDevice, ConnectedDeviceEventArgs args) + { + if (args.EventId == ConnectedDeviceEventIds.ManufacturerEventId) + { + EdidManufacturerFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.NameEventId) + { + EdidNameFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.PreferredTimingEventId) + { + EdidPreferredTimingFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.SerialNumberEventId) + { + EdidSerialNumberFeedback.FireUpdate(); + } + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + /// + /// Gets the CEC stream directly from the HDMI port. + /// + public Cec StreamCec { get { return Rmc.HdmiOutput.StreamCec; } } + #endregion + + #region IRelayPorts Members + + public int NumberOfRelayPorts + { + get { return Rmc.NumberOfRelayPorts; } + } + + public CrestronCollection RelayPorts + { + get { return Rmc.RelayPorts; } + } + + #endregion + + #region IBasicVolumeWithFeedback Members + + public BoolFeedback MuteFeedback + { + get; + private set; + } + + /// + /// Not implemented + /// + public void MuteOff() + { + } + + /// + /// Not implemented + /// + public void MuteOn() + { + } + + public void SetVolume(ushort level) + { + Rmc.AudioOutput.Volume.UShortValue = level; + } + + public IntFeedback VolumeLevelFeedback + { + get; + private set; + } + + #endregion + + #region IBasicVolumeControls Members + + /// + /// Not implemented + /// + public void MuteToggle() + { + } + + public void VolumeDown(bool pressRelease) + { + if (pressRelease) + SigHelper.RampTimeScaled(Rmc.AudioOutput.Volume, 0, 4000); + else + Rmc.AudioOutput.Volume.StopRamp(); + } + + public void VolumeUp(bool pressRelease) + { + if (pressRelease) + SigHelper.RampTimeScaled(Rmc.AudioOutput.Volume, 65535, 4000); + else + Rmc.AudioOutput.Volume.StopRamp(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4k100C1GController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4k100C1GController.cs new file mode 100644 index 00000000..571fa786 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4k100C1GController.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + + +namespace PepperDash.Essentials.DM +{ + public class DmRmc4k100C1GController : DmHdBaseTControllerBase, IRoutingInputsOutputs, + IIROutputPorts, IComPorts, ICec + { + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut }; } + } + + public DmRmc4k100C1GController(string key, string name, DmRmc4K100C1G rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + // Set Ports for CEC + HdmiOut.Port = Rmc; // Unique case, this class has no HdmiOutput port and ICec is implemented on the receiver class itself + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return (Rmc as DmRmc4K100C1G).IROutputPorts; } } + public int NumberOfIROutputPorts { get { return (Rmc as DmRmc4K100C1G).NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + public Cec StreamCec { get { return (Rmc as DmRmc4K100C1G).StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4kScalerCDspController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4kScalerCDspController.cs new file mode 100644 index 00000000..788b0e74 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmc4kScalerCDspController.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmRmc4kScalerCDspController : DmRmcControllerBase, IRoutingInputsOutputs, IBasicVolumeWithFeedback, + IIROutputPorts, IComPorts, ICec, IRelayPorts + { + public DmRmc4kScalerCDsp Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + public RoutingOutputPort BalancedAudioOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut, BalancedAudioOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmc4kScalerCDspController(string key, string name, DmRmc4kScalerCDsp rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + BalancedAudioOut = new RoutingOutputPort(DmPortName.BalancedAudioOut, eRoutingSignalType.Audio, + eRoutingPortConnectionType.LineAudio, null, this); + + MuteFeedback = new BoolFeedback(() => false); + VolumeLevelFeedback = new IntFeedback("MainVolumeLevelFeedback", () => + rmc.AudioOutput.VolumeFeedback.UShortValue); + + EdidManufacturerFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Manufacturer.StringValue); + EdidNameFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Name.StringValue); + EdidPreferredTimingFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.PreferredTiming.StringValue); + EdidSerialNumberFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.SerialNumber.StringValue); + + VideoOutputResolutionFeedback = new StringFeedback(() => Rmc.HdmiOutput.GetVideoResolutionString()); + + Rmc.HdmiOutput.OutputStreamChange += HdmiOutput_OutputStreamChange; + Rmc.HdmiOutput.ConnectedDevice.DeviceInformationChange += ConnectedDevice_DeviceInformationChange; + + // Set Ports for CEC + HdmiOut.Port = Rmc.HdmiOutput; + } + + void HdmiOutput_OutputStreamChange(EndpointOutputStream outputStream, EndpointOutputStreamEventArgs args) + { + if (args.EventId == EndpointOutputStreamEventIds.HorizontalResolutionFeedbackEventId || args.EventId == EndpointOutputStreamEventIds.VerticalResolutionFeedbackEventId || + args.EventId == EndpointOutputStreamEventIds.FramesPerSecondFeedbackEventId) + { + VideoOutputResolutionFeedback.FireUpdate(); + } + } + + void ConnectedDevice_DeviceInformationChange(ConnectedDeviceInformation connectedDevice, ConnectedDeviceEventArgs args) + { + if (args.EventId == ConnectedDeviceEventIds.ManufacturerEventId) + { + EdidManufacturerFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.NameEventId) + { + EdidNameFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.PreferredTimingEventId) + { + EdidPreferredTimingFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.SerialNumberEventId) + { + EdidSerialNumberFeedback.FireUpdate(); + } + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + /// + /// Gets the CEC stream directly from the HDMI port. + /// + public Cec StreamCec { get { return Rmc.HdmiOutput.StreamCec; } } + #endregion + + #region IRelayPorts Members + + public int NumberOfRelayPorts + { + get { return Rmc.NumberOfRelayPorts; } + } + + public CrestronCollection RelayPorts + { + get { return Rmc.RelayPorts; } + } + + #endregion + + #region IBasicVolumeWithFeedback Members + + public BoolFeedback MuteFeedback + { + get; + private set; + } + + /// + /// Not implemented + /// + public void MuteOff() + { + } + + /// + /// Not implemented + /// + public void MuteOn() + { + } + + public void SetVolume(ushort level) + { + Rmc.AudioOutput.Volume.UShortValue = level; + } + + public IntFeedback VolumeLevelFeedback + { + get; + private set; + } + + #endregion + + #region IBasicVolumeControls Members + + /// + /// Not implemented + /// + public void MuteToggle() + { + } + + public void VolumeDown(bool pressRelease) + { + if (pressRelease) + SigHelper.RampTimeScaled(Rmc.AudioOutput.Volume, 0, 4000); + else + Rmc.AudioOutput.Volume.StopRamp(); + } + + public void VolumeUp(bool pressRelease) + { + if (pressRelease) + SigHelper.RampTimeScaled(Rmc.AudioOutput.Volume, 65535, 4000); + else + Rmc.AudioOutput.Volume.StopRamp(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs new file mode 100644 index 00000000..16be9593 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcHelper.cs @@ -0,0 +1,250 @@ +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.Receivers; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM +{ + public abstract class DmRmcControllerBase : CrestronGenericBaseDevice + { + public virtual StringFeedback VideoOutputResolutionFeedback { get; protected set; } + public virtual StringFeedback EdidManufacturerFeedback { get; protected set; } + public virtual StringFeedback EdidNameFeedback { get; protected set; } + public virtual StringFeedback EdidPreferredTimingFeedback { get; protected set; } + public virtual StringFeedback EdidSerialNumberFeedback { get; protected set; } + + public DmRmcControllerBase(string key, string name, EndpointReceiverBase device) + : base(key, name, device) + { + // if wired to a chassis, skip registration step in base class + if (device.DMOutput != null) + { + this.PreventRegistration = true; + } + AddToFeedbackList(VideoOutputResolutionFeedback, EdidManufacturerFeedback, EdidSerialNumberFeedback, EdidNameFeedback, EdidPreferredTimingFeedback); + } + } + + public abstract class DmHdBaseTControllerBase : CrestronGenericBaseDevice + { + public HDBaseTBase Rmc { get; protected set; } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmHdBaseTControllerBase(string key, string name, HDBaseTBase rmc) + : base(key, name, rmc) + { + + } + } + + public class DmRmcHelper + { + /// + /// A factory method for various DmTxControllers + /// + /// + /// + /// + /// + public static CrestronGenericBaseDevice GetDmRmcController(string key, string name, string typeName, DmRmcPropertiesConfig props) + { + // switch on type name... later... + + typeName = typeName.ToLower(); + uint ipid = props.Control.IpIdInt; // Convert.ToUInt16(props.Id, 16); + + + + // right here, we need to grab the tie line that associates this + // RMC with a chassis or processor. If the RMC input's tie line is not + // connected to a chassis, then it's parent is the processor. + // If the RMC is connected to a chassis, then we need to grab the + // output number from the tie line and use that to plug it in. + // Example of chassis-connected: + //{ + // "sourceKey": "dmMd8x8-1", + // "sourcePort": "anyOut2", + // "destinationKey": "dmRmc100C-2", + // "destinationPort": "DmIn" + //} + + // Tx -> RMC link: + //{ + // "sourceKey": "dmTx201C-1", + // "sourcePort": "DmOut", + // "destinationKey": "dmRmc100C-2", + // "destinationPort": "DmIn" + //} + + var tlc = TieLineCollection.Default; + // grab the tie line that has this key as + // THIS DOESN'T WORK BECAUSE THE RMC THAT WE NEED (THIS) HASN'T BEEN MADE + // YET AND THUS WILL NOT HAVE A TIE LINE... + var inputTieLine = tlc.FirstOrDefault(t => + { + var d = t.DestinationPort.ParentDevice; + return d.Key.Equals(key, StringComparison.OrdinalIgnoreCase) + && d is DmChassisController; + }); + + var pKey = props.ParentDeviceKey.ToLower(); + + + + + // Non-DM-chassis endpoints + if (pKey == "processor") + { + // Catch constructor failures, mainly dues to IPID + try + { + if (typeName.StartsWith("dmrmc100c")) + return new DmRmcX100CController(key, name, new DmRmc100C(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmc4k100c")) + return new DmRmcX100CController(key, name, new DmRmc4k100C(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmc4kz100c")) + return new DmRmcX100CController(key, name, new DmRmc4kz100C(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmc150s")) + return new DmRmc150SController(key, name, new DmRmc150S(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmc200c")) + return new DmRmc200CController(key, name, new DmRmc200C(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmc200s")) + return new DmRmc200SController(key, name, new DmRmc200S(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmc200s2")) + return new DmRmc200S2Controller(key, name, new DmRmc200S2(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmcscalerc")) + return new DmRmcScalerCController(key, name, new DmRmcScalerC(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmcscalers")) + return new DmRmcScalerSController(key, name, new DmRmcScalerS(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmcscalers2")) + return new DmRmcScalerS2Controller(key, name, new DmRmcScalerS2(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmc4kscalerc")) + return new DmRmc4kScalerCController(key, name, new DmRmc4kScalerC(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmrmc4kscalercdsp")) + return new DmRmc4kScalerCDspController(key, name, new DmRmc4kScalerCDsp(ipid, Global.ControlSystem)); + } + catch (Exception e) + { + Debug.Console(0, "[{0}] WARNING: Cannot create DM-RMC device: {1}", key, e.Message); + } + + + Debug.Console(0, "Cannot create DM-RMC of type: '{0}'", typeName); + } + // Endpoints attached to DM Chassis + else + { + var parentDev = DeviceManager.GetDeviceForKey(pKey); + if (!(parentDev is DmChassisController)) + { + Debug.Console(0, "Cannot create DM device '{0}'. '{1}' is not a DM Chassis.", + key, pKey); + return null; + } + + var chassis = (parentDev as DmChassisController).Chassis; + var num = props.ParentOutputNumber; + if (num <= 0 || num > chassis.NumberOfOutputs) + { + Debug.Console(0, "Cannot create DM device '{0}'. Output number '{1}' is out of range", + key, num); + return null; + } + else + { + var controller = (parentDev as DmChassisController); + controller.RxDictionary.Add(num, key); + } + // Catch constructor failures, mainly dues to IPID + try + { + + // Must use different constructor for CPU3 chassis types. No IPID + if (chassis is DmMd8x8Cpu3 || chassis is DmMd16x16Cpu3 || + chassis is DmMd32x32Cpu3 || chassis is DmMd8x8Cpu3rps || + chassis is DmMd16x16Cpu3rps || chassis is DmMd32x32Cpu3rps) + { + if (typeName.StartsWith("hdbasetrx")) + return new HDBaseTRxController(key, name, new HDRx3CB(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc4k100c1g")) + return new DmRmc4k100C1GController(key, name, new DmRmc4K100C1G(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc100c")) + return new DmRmcX100CController(key, name, new DmRmc100C(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc4k100c")) + return new DmRmcX100CController(key, name, new DmRmc4k100C(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc4kz100c")) + return new DmRmcX100CController(key, name, new DmRmc4kz100C(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc150s")) + return new DmRmc150SController(key, name, new DmRmc150S(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc200c")) + return new DmRmc200CController(key, name, new DmRmc200C(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc200s")) + return new DmRmc200SController(key, name, new DmRmc200S(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc200s2")) + return new DmRmc200S2Controller(key, name, new DmRmc200S2(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmcscalerc")) + return new DmRmcScalerCController(key, name, new DmRmcScalerC(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmcscalers")) + return new DmRmcScalerSController(key, name, new DmRmcScalerS(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmcscalers2")) + return new DmRmcScalerS2Controller(key, name, new DmRmcScalerS2(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc4kscalerc")) + return new DmRmc4kScalerCController(key, name, new DmRmc4kScalerC(chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc4kscalercdsp")) + return new DmRmc4kScalerCDspController(key, name, new DmRmc4kScalerCDsp(chassis.Outputs[num])); + } + else + { + if (typeName.StartsWith("hdbasetrx")) + return new HDBaseTRxController(key, name, new HDRx3CB(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc4k100c1g")) + return new DmRmc4k100C1GController(key, name, new DmRmc4K100C1G(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc100c")) + return new DmRmcX100CController(key, name, new DmRmc100C(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc4k100c")) + return new DmRmcX100CController(key, name, new DmRmc4k100C(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc4kz100c")) + return new DmRmcX100CController(key, name, new DmRmc4kz100C(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc150s")) + return new DmRmc150SController(key, name, new DmRmc150S(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc200c")) + return new DmRmc200CController(key, name, new DmRmc200C(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc200s")) + return new DmRmc200SController(key, name, new DmRmc200S(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc200s2")) + return new DmRmc200S2Controller(key, name, new DmRmc200S2(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmcscalerc")) + return new DmRmcScalerCController(key, name, new DmRmcScalerC(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmcscalers")) + return new DmRmcScalerSController(key, name, new DmRmcScalerS(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmcscalers2")) + return new DmRmcScalerS2Controller(key, name, new DmRmcScalerS2(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc4kscalerc")) + return new DmRmc4kScalerCController(key, name, new DmRmc4kScalerC(ipid, chassis.Outputs[num])); + if (typeName.StartsWith("dmrmc4kscalercdsp")) + return new DmRmc4kScalerCDspController(key, name, new DmRmc4kScalerCDsp(ipid, chassis.Outputs[num])); + } + } + catch (Exception e) + { + Debug.Console(0, "[{0}] WARNING: Cannot create DM-RMC device: {1}", key, e.Message); + } + } + + return null; + } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcScalerCController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcScalerCController.cs new file mode 100644 index 00000000..a7c8407a --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcScalerCController.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmRmcScalerCController : DmRmcControllerBase, IRoutingInputsOutputs, + IIROutputPorts, IComPorts, ICec + { + public DmRmcScalerC Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmcScalerCController(string key, string name, DmRmcScalerC rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + EdidManufacturerFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Manufacturer.StringValue); + EdidNameFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Name.StringValue); + EdidPreferredTimingFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.PreferredTiming.StringValue); + EdidSerialNumberFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.SerialNumber.StringValue); + + VideoOutputResolutionFeedback = new StringFeedback(() => Rmc.HdmiOutput.GetVideoResolutionString()); + + Rmc.HdmiOutput.OutputStreamChange += HdmiOutput_OutputStreamChange; + Rmc.HdmiOutput.ConnectedDevice.DeviceInformationChange += ConnectedDevice_DeviceInformationChange; + + // Set Ports for CEC + HdmiOut.Port = Rmc.HdmiOutput; + } + + void HdmiOutput_OutputStreamChange(EndpointOutputStream outputStream, EndpointOutputStreamEventArgs args) + { + if (args.EventId == EndpointOutputStreamEventIds.HorizontalResolutionFeedbackEventId || args.EventId == EndpointOutputStreamEventIds.VerticalResolutionFeedbackEventId || + args.EventId == EndpointOutputStreamEventIds.FramesPerSecondFeedbackEventId) + { + VideoOutputResolutionFeedback.FireUpdate(); + } + } + + void ConnectedDevice_DeviceInformationChange(ConnectedDeviceInformation connectedDevice, ConnectedDeviceEventArgs args) + { + if (args.EventId == ConnectedDeviceEventIds.ManufacturerEventId) + { + EdidManufacturerFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.NameEventId) + { + EdidNameFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.PreferredTimingEventId) + { + EdidPreferredTimingFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.SerialNumberEventId) + { + EdidSerialNumberFeedback.FireUpdate(); + } + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + /// + /// Gets the CEC stream directly from the HDMI port. + /// + public Cec StreamCec { get { return Rmc.HdmiOutput.StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcScalerS2Controller.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcScalerS2Controller.cs new file mode 100644 index 00000000..d4c574dd --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcScalerS2Controller.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmRmcScalerS2Controller : DmRmcControllerBase, IRoutingInputsOutputs, + IIROutputPorts, IComPorts, ICec + { + public DmRmcScalerS2 Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmcScalerS2Controller(string key, string name, DmRmcScalerS2 rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + EdidManufacturerFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Manufacturer.StringValue); + EdidNameFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Name.StringValue); + EdidPreferredTimingFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.PreferredTiming.StringValue); + EdidSerialNumberFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.SerialNumber.StringValue); + + VideoOutputResolutionFeedback = new StringFeedback(() => Rmc.HdmiOutput.GetVideoResolutionString()); + + Rmc.HdmiOutput.OutputStreamChange += HdmiOutput_OutputStreamChange; + Rmc.HdmiOutput.ConnectedDevice.DeviceInformationChange += ConnectedDevice_DeviceInformationChange; + + // Set Ports for CEC + HdmiOut.Port = Rmc.HdmiOutput; + } + + void HdmiOutput_OutputStreamChange(EndpointOutputStream outputStream, EndpointOutputStreamEventArgs args) + { + if (args.EventId == EndpointOutputStreamEventIds.HorizontalResolutionFeedbackEventId || args.EventId == EndpointOutputStreamEventIds.VerticalResolutionFeedbackEventId || + args.EventId == EndpointOutputStreamEventIds.FramesPerSecondFeedbackEventId) + { + VideoOutputResolutionFeedback.FireUpdate(); + } + } + + void ConnectedDevice_DeviceInformationChange(ConnectedDeviceInformation connectedDevice, ConnectedDeviceEventArgs args) + { + if (args.EventId == ConnectedDeviceEventIds.ManufacturerEventId) + { + EdidManufacturerFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.NameEventId) + { + EdidNameFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.PreferredTimingEventId) + { + EdidPreferredTimingFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.SerialNumberEventId) + { + EdidSerialNumberFeedback.FireUpdate(); + } + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + /// + /// Gets the CEC stream directly from the HDMI port. + /// + public Cec StreamCec { get { return Rmc.HdmiOutput.StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcScalerSController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcScalerSController.cs new file mode 100644 index 00000000..12fdf554 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcScalerSController.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions + /// + /// + public class DmRmcScalerSController : DmRmcControllerBase, IRoutingInputsOutputs, + IIROutputPorts, IComPorts, ICec + { + public DmRmcScalerS Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmcScalerSController(string key, string name, DmRmcScalerS rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + EdidManufacturerFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Manufacturer.StringValue); + EdidNameFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.Name.StringValue); + EdidPreferredTimingFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.PreferredTiming.StringValue); + EdidSerialNumberFeedback = new StringFeedback(() => Rmc.HdmiOutput.ConnectedDevice.SerialNumber.StringValue); + + VideoOutputResolutionFeedback = new StringFeedback(() => Rmc.HdmiOutput.GetVideoResolutionString()); + + Rmc.HdmiOutput.OutputStreamChange += HdmiOutput_OutputStreamChange; + Rmc.HdmiOutput.ConnectedDevice.DeviceInformationChange += ConnectedDevice_DeviceInformationChange; + + // Set Ports for CEC + HdmiOut.Port = Rmc.HdmiOutput; + } + + void HdmiOutput_OutputStreamChange(EndpointOutputStream outputStream, EndpointOutputStreamEventArgs args) + { + if (args.EventId == EndpointOutputStreamEventIds.HorizontalResolutionFeedbackEventId || args.EventId == EndpointOutputStreamEventIds.VerticalResolutionFeedbackEventId || + args.EventId == EndpointOutputStreamEventIds.FramesPerSecondFeedbackEventId) + { + VideoOutputResolutionFeedback.FireUpdate(); + } + } + + void ConnectedDevice_DeviceInformationChange(ConnectedDeviceInformation connectedDevice, ConnectedDeviceEventArgs args) + { + if (args.EventId == ConnectedDeviceEventIds.ManufacturerEventId) + { + EdidManufacturerFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.NameEventId) + { + EdidNameFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.PreferredTimingEventId) + { + EdidPreferredTimingFeedback.FireUpdate(); + } + else if (args.EventId == ConnectedDeviceEventIds.SerialNumberEventId) + { + EdidSerialNumberFeedback.FireUpdate(); + } + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + /// + /// Gets the CEC stream directly from the HDMI port. + /// + public Cec StreamCec { get { return Rmc.HdmiOutput.StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcX100CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcX100CController.cs new file mode 100644 index 00000000..4b21ce3e --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Receivers/DmRmcX100CController.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// Builds a controller for basic DM-RMCs (both 4K and non-4K) with Com and IR ports and no control functions + /// + /// + public class DmRmcX100CController : DmRmcControllerBase, IRoutingInputsOutputs, + IIROutputPorts, IComPorts, ICec + { + public DmRmc100C Rmc { get; private set; } + + public RoutingInputPort DmIn { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { DmIn }; } + } + + public RoutingPortCollection OutputPorts + { + get { return new RoutingPortCollection { HdmiOut }; } + } + + /// + /// Make a Crestron RMC and put it in here + /// + public DmRmcX100CController(string key, string name, DmRmc100C rmc) + : base(key, name, rmc) + { + Rmc = rmc; + DmIn = new RoutingInputPort(DmPortName.DmIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, 0, this); + HdmiOut = new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + // Set Ports for CEC + HdmiOut.Port = Rmc; // Unique case, this class has no HdmiOutput port and ICec is implemented on the receiver class itself + } + + public override bool CustomActivate() + { + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Rmc.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Rmc.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Rmc.ComPorts; } } + public int NumberOfComPorts { get { return Rmc.NumberOfComPorts; } } + #endregion + + #region ICec Members + public Cec StreamCec { get { return Rmc.StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx200Controller.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx200Controller.cs new file mode 100644 index 00000000..11b2f1b4 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx200Controller.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM +{ + // using eVst = Crestron.SimplSharpPro.DeviceSupport.eX02VideoSourceType; + + /// + /// Controller class for all DM-TX-201C/S/F transmitters + /// + public class DmTx200Controller : DmTxControllerBase, ITxRouting, IHasFeedback + { + public DmTx200C2G Tx { get; private set; } + + public RoutingInputPortWithVideoStatuses HdmiInput { get; private set; } + public RoutingInputPortWithVideoStatuses VgaInput { get; private set; } + public RoutingOutputPort DmOutput { get; private set; } + + public override StringFeedback ActiveVideoInputFeedback { get; protected set; } + public IntFeedback VideoSourceNumericFeedback { get; protected set; } + public IntFeedback AudioSourceNumericFeedback { get; protected set; } + public IntFeedback HdmiInHdcpCapabilityFeedback { get; protected set; } + + //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } + //public override ushort HdcpSupportCapability { get; protected set; } + + /// + /// Helps get the "real" inputs, including when in Auto + /// + public DmTx200Base.eSourceSelection ActualActiveVideoInput + { + get + { + if (Tx.VideoSourceFeedback == DmTx200Base.eSourceSelection.Digital || + Tx.VideoSourceFeedback == DmTx200Base.eSourceSelection.Analog || + Tx.VideoSourceFeedback == DmTx200Base.eSourceSelection.Disable) + return Tx.VideoSourceFeedback; + else // auto + { + if (Tx.HdmiInput.SyncDetectedFeedback.BoolValue) + return DmTx200Base.eSourceSelection.Digital; + else if (Tx.VgaInput.SyncDetectedFeedback.BoolValue) + return DmTx200Base.eSourceSelection.Analog; + else + return DmTx200Base.eSourceSelection.Disable; + } + } + } + + public RoutingPortCollection InputPorts + { + get + { + return new RoutingPortCollection + { + HdmiInput, + VgaInput, + AnyVideoInput + }; + } + } + + public RoutingPortCollection OutputPorts + { + get + { + return new RoutingPortCollection { DmOutput }; + } + } + + /// + /// + /// + /// + /// + /// + public DmTx200Controller(string key, string name, DmTx200C2G tx) + : base(key, name, tx) + { + Tx = tx; + + HdmiInput = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, DmTx200Base.eSourceSelection.Digital, this, + VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInput)); + VgaInput = new RoutingInputPortWithVideoStatuses(DmPortName.VgaIn, + eRoutingSignalType.Video, eRoutingPortConnectionType.Vga, DmTx200Base.eSourceSelection.Analog, this, + VideoStatusHelper.GetVgaInputStatusFuncs(tx.VgaInput)); + + ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", + () => ActualActiveVideoInput.ToString()); + + Tx.HdmiInput.InputStreamChange += new EndpointInputStreamChangeEventHandler(InputStreamChangeEvent); + Tx.BaseEvent += Tx_BaseEvent; + Tx.OnlineStatusChange += new OnlineStatusChangeEventHandler(Tx_OnlineStatusChange); + + VideoSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.VideoSourceFeedback; + }); + AudioSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.AudioSourceFeedback; + }); + + HdmiInHdcpCapabilityFeedback = new IntFeedback("HdmiInHdcpCapability", () => + { + if (tx.HdmiInput.HdpcSupportOnFeedback.BoolValue) + return 1; + else + return 0; + }); + + HdcpSupportCapability = eHdcpCapabilityType.HdcpAutoSupport; + + var combinedFuncs = new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => + (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Digital + && tx.HdmiInput.VideoAttributes.HdcpActiveFeedback.BoolValue), + + HdcpStateFeedbackFunc = () => + { + if (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Digital) + return tx.HdmiInput.VideoAttributes.HdcpStateFeedback.ToString(); + return ""; + }, + + VideoResolutionFeedbackFunc = () => + { + if (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Digital) + return tx.HdmiInput.VideoAttributes.GetVideoResolutionString(); + if (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Analog) + return tx.VgaInput.VideoAttributes.GetVideoResolutionString(); + return ""; + }, + VideoSyncFeedbackFunc = () => + (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Digital + && tx.HdmiInput.SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Analog + && tx.VgaInput.SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Auto + && (tx.VgaInput.SyncDetectedFeedback.BoolValue || tx.HdmiInput.SyncDetectedFeedback.BoolValue)) + + }; + + AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.None, 0, this, combinedFuncs); + + DmOutput = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmCat, null, this); + + AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, + AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, + AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, + AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiInHdcpCapabilityFeedback); + + // Set Ports for CEC + HdmiInput.Port = Tx.HdmiInput; + VgaInput.Port = Tx.VgaInput; + DmOutput.Port = Tx.DmOutput; + } + + void Tx_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) + { + ActiveVideoInputFeedback.FireUpdate(); + VideoSourceNumericFeedback.FireUpdate(); + AudioSourceNumericFeedback.FireUpdate(); + + } + + public override bool CustomActivate() + { + + Tx.HdmiInput.InputStreamChange += (o, a) => FowardInputStreamChange(HdmiInput, a.EventId); + Tx.HdmiInput.VideoAttributes.AttributeChange += (o, a) => FireVideoAttributeChange(HdmiInput, a.EventId); + + Tx.VgaInput.InputStreamChange += (o, a) => FowardInputStreamChange(VgaInput, a.EventId); + Tx.VgaInput.VideoAttributes.AttributeChange += (o, a) => FireVideoAttributeChange(VgaInput, a.EventId); + + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) + { + Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); + + switch (input) + { + case 0: + { + ExecuteSwitch(DmTx200Base.eSourceSelection.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(HdmiInput.Selector, null, type); + break; + } + case 2: + { + ExecuteSwitch(VgaInput.Selector, null, type); + break; + } + case 3: + { + ExecuteSwitch(DmTx200Base.eSourceSelection.Disable, null, type); + break; + } + } + } + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + if ((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) + Tx.VideoSource = (DmTx200Base.eSourceSelection)inputSelector; + if ((signalType | eRoutingSignalType.Audio) == eRoutingSignalType.Audio) + Tx.AudioSource = (DmTx200Base.eSourceSelection)inputSelector; + } + + void Tx_BaseEvent(GenericBase device, BaseEventArgs args) + { + var id = args.EventId; + Debug.Console(2, this, "EventId {0}", args.EventId); + + if (id == EndpointTransmitterBase.VideoSourceFeedbackEventId) + { + Debug.Console(2, this, " Video Source: {0}", Tx.VideoSourceFeedback); + VideoSourceNumericFeedback.FireUpdate(); + ActiveVideoInputFeedback.FireUpdate(); + } + + // ------------------------------ incomplete ----------------------------------------- + else if (id == EndpointTransmitterBase.AudioSourceFeedbackEventId) + { + Debug.Console(2, this, " Audio Source: {0}", Tx.AudioSourceFeedback); + AudioSourceNumericFeedback.FireUpdate(); + } + } + + void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) + { + Debug.Console(2, "{0} event {1} stream {2}", this.Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); + + if (args.EventId == EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId) + { + HdmiInHdcpCapabilityFeedback.FireUpdate(); + } + else if (args.EventId == EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId) + { + HdmiInHdcpCapabilityFeedback.FireUpdate(); + } + } + + /// + /// Relays the input stream change to the appropriate RoutingInputPort. + /// + void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + if (eventId == EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) + { + inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); + } + } + + /// + /// Relays the VideoAttributes change to a RoutingInputPort + /// + void FireVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds + //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", + // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); + switch (eventId) + { + case VideoAttributeEventIds.HdcpActiveFeedbackEventId: + inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HdcpStateFeedbackEventId: + inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: + case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + } + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx201CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx201CController.cs new file mode 100644 index 00000000..fb10829c --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx201CController.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM +{ + // using eVst = Crestron.SimplSharpPro.DeviceSupport.eX02VideoSourceType; + + /// + /// Controller class for all DM-TX-201C/S/F transmitters + /// + public class DmTx201XController : DmTxControllerBase, ITxRouting, IHasFeedback + { + public DmTx201S Tx { get; private set; } // uses the 201S class as it is the base class for the 201C + + public RoutingInputPortWithVideoStatuses HdmiInput { get; private set; } + public RoutingInputPortWithVideoStatuses VgaInput { get; private set; } + public RoutingOutputPort DmOutput { get; private set; } + public RoutingOutputPort HdmiLoopOut { get; private set; } + + public override StringFeedback ActiveVideoInputFeedback { get; protected set; } + public IntFeedback VideoSourceNumericFeedback { get; protected set; } + public IntFeedback AudioSourceNumericFeedback { get; protected set; } + public IntFeedback HdmiInHdcpCapabilityFeedback { get; protected set; } + + //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } + //public override ushort HdcpSupportCapability { get; protected set; } + + /// + /// Helps get the "real" inputs, including when in Auto + /// + public DmTx200Base.eSourceSelection ActualActiveVideoInput + { + get + { + if (Tx.VideoSourceFeedback == DmTx200Base.eSourceSelection.Digital || + Tx.VideoSourceFeedback == DmTx200Base.eSourceSelection.Analog || + Tx.VideoSourceFeedback == DmTx200Base.eSourceSelection.Disable) + return Tx.VideoSourceFeedback; + else // auto + { + if (Tx.HdmiInput.SyncDetectedFeedback.BoolValue) + return DmTx200Base.eSourceSelection.Digital; + else if (Tx.VgaInput.SyncDetectedFeedback.BoolValue) + return DmTx200Base.eSourceSelection.Analog; + else + return DmTx200Base.eSourceSelection.Disable; + } + } + } + + public RoutingPortCollection InputPorts + { + get + { + return new RoutingPortCollection + { + HdmiInput, + VgaInput, + AnyVideoInput + }; + } + } + + public RoutingPortCollection OutputPorts + { + get + { + return new RoutingPortCollection { DmOutput, HdmiLoopOut }; + } + } + + /// + /// + /// + /// + /// + /// + public DmTx201XController(string key, string name, DmTx201S tx) + : base(key, name, tx) + { + Tx = tx; + + HdmiInput = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, DmTx200Base.eSourceSelection.Digital, this, + VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInput)); + VgaInput = new RoutingInputPortWithVideoStatuses(DmPortName.VgaIn, + eRoutingSignalType.Video, eRoutingPortConnectionType.Vga, DmTx200Base.eSourceSelection.Analog, this, + VideoStatusHelper.GetVgaInputStatusFuncs(tx.VgaInput)); + + ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", + () => ActualActiveVideoInput.ToString()); + + Tx.HdmiInput.InputStreamChange += new EndpointInputStreamChangeEventHandler(InputStreamChangeEvent); + Tx.BaseEvent += Tx_BaseEvent; + Tx.OnlineStatusChange += new OnlineStatusChangeEventHandler(Tx_OnlineStatusChange); + + VideoSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.VideoSourceFeedback; + }); + AudioSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.AudioSourceFeedback; + }); + + HdmiInHdcpCapabilityFeedback = new IntFeedback("HdmiInHdcpCapability", () => + { + if (tx.HdmiInput.HdpcSupportOnFeedback.BoolValue) + return 1; + else + return 0; + }); + + HdcpSupportCapability = eHdcpCapabilityType.HdcpAutoSupport; + + var combinedFuncs = new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => + (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Digital + && tx.HdmiInput.VideoAttributes.HdcpActiveFeedback.BoolValue), + + HdcpStateFeedbackFunc = () => + { + if (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Digital) + return tx.HdmiInput.VideoAttributes.HdcpStateFeedback.ToString(); + return ""; + }, + + VideoResolutionFeedbackFunc = () => + { + if (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Digital) + return tx.HdmiInput.VideoAttributes.GetVideoResolutionString(); + if (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Analog) + return tx.VgaInput.VideoAttributes.GetVideoResolutionString(); + return ""; + }, + VideoSyncFeedbackFunc = () => + (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Digital + && tx.HdmiInput.SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Analog + && tx.VgaInput.SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == DmTx200Base.eSourceSelection.Auto + && (tx.VgaInput.SyncDetectedFeedback.BoolValue || tx.HdmiInput.SyncDetectedFeedback.BoolValue)) + + }; + + AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.None, 0, this, combinedFuncs); + + DmOutput = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmCat, null, this); + HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, + AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, + AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, + AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiInHdcpCapabilityFeedback); + + // Set Ports for CEC + HdmiInput.Port = Tx.HdmiInput; + VgaInput.Port = Tx.VgaInput; + HdmiLoopOut.Port = Tx.HdmiOutput; + DmOutput.Port = Tx.DmOutput; + } + + void Tx_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) + { + ActiveVideoInputFeedback.FireUpdate(); + VideoSourceNumericFeedback.FireUpdate(); + AudioSourceNumericFeedback.FireUpdate(); + + } + + public override bool CustomActivate() + { + + Tx.HdmiInput.InputStreamChange += (o, a) => FowardInputStreamChange(HdmiInput, a.EventId); + Tx.HdmiInput.VideoAttributes.AttributeChange += (o, a) => FireVideoAttributeChange(HdmiInput, a.EventId); + + Tx.VgaInput.InputStreamChange += (o, a) => FowardInputStreamChange(VgaInput, a.EventId); + Tx.VgaInput.VideoAttributes.AttributeChange += (o, a) => FireVideoAttributeChange(VgaInput, a.EventId); + + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) + { + Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); + + switch (input) + { + case 0: + { + ExecuteSwitch(DmTx200Base.eSourceSelection.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(HdmiInput.Selector, null, type); + break; + } + case 2: + { + ExecuteSwitch(VgaInput.Selector, null, type); + break; + } + case 3: + { + ExecuteSwitch(DmTx200Base.eSourceSelection.Disable, null, type); + break; + } + } + } + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + if((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) + Tx.VideoSource = (DmTx200Base.eSourceSelection)inputSelector; + if ((signalType | eRoutingSignalType.Audio) == eRoutingSignalType.Audio) + Tx.AudioSource = (DmTx200Base.eSourceSelection)inputSelector; + } + + void Tx_BaseEvent(GenericBase device, BaseEventArgs args) + { + var id = args.EventId; + Debug.Console(2, this, "EventId {0}", args.EventId); + + if (id == EndpointTransmitterBase.VideoSourceFeedbackEventId) + { + Debug.Console(2, this, " Video Source: {0}", Tx.VideoSourceFeedback); + VideoSourceNumericFeedback.FireUpdate(); + ActiveVideoInputFeedback.FireUpdate(); + } + + // ------------------------------ incomplete ----------------------------------------- + else if (id == EndpointTransmitterBase.AudioSourceFeedbackEventId) + { + Debug.Console(2, this, " Audio Source: {0}", Tx.AudioSourceFeedback); + AudioSourceNumericFeedback.FireUpdate(); + } + } + + void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) + { + Debug.Console(2, "{0} event {1} stream {2}", this.Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); + + if (args.EventId == EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId) + { + HdmiInHdcpCapabilityFeedback.FireUpdate(); + } + else if (args.EventId == EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId) + { + HdmiInHdcpCapabilityFeedback.FireUpdate(); + } + } + + /// + /// Relays the input stream change to the appropriate RoutingInputPort. + /// + void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + if (eventId == EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) + { + inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); + } + } + + /// + /// Relays the VideoAttributes change to a RoutingInputPort + /// + void FireVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds + //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", + // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); + switch (eventId) + { + case VideoAttributeEventIds.HdcpActiveFeedbackEventId: + inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HdcpStateFeedbackEventId: + inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: + case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + } + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx401CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx401CController.cs new file mode 100644 index 00000000..eb5c10fc --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx401CController.cs @@ -0,0 +1,323 @@ +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; +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM +{ + using eVst = DmTx401C.eSourceSelection; + + public class DmTx401CController : DmTxControllerBase, ITxRouting, IHasFeedback, IIROutputPorts, IComPorts + { + public DmTx401C Tx { get; private set; } + + public RoutingInputPortWithVideoStatuses HdmiIn { get; private set; } + public RoutingInputPortWithVideoStatuses DisplayPortIn { get; private set; } + public RoutingInputPortWithVideoStatuses VgaIn { get; private set; } + public RoutingInputPortWithVideoStatuses CompositeIn { get; private set; } + public RoutingOutputPort DmOut { get; private set; } + + public override StringFeedback ActiveVideoInputFeedback { get; protected set; } + public IntFeedback VideoSourceNumericFeedback { get; protected set; } + public IntFeedback AudioSourceNumericFeedback { get; protected set; } + public IntFeedback HdmiInHdcpCapabilityFeedback { get; protected set; } + + /// + /// Helps get the "real" inputs, including when in Auto + /// + public BaseDmTx401.eSourceSelection ActualVideoInput + { + get + { + if (Tx.VideoSourceFeedback != BaseDmTx401.eSourceSelection.Auto) + return Tx.VideoSourceFeedback; + else // auto + { + if (Tx.HdmiInput.SyncDetectedFeedback.BoolValue) + return BaseDmTx401.eSourceSelection.HDMI; + else if (Tx.VgaInput.SyncDetectedFeedback.BoolValue) + return BaseDmTx401.eSourceSelection.VGA; + else if (Tx.DisplayPortInput.SyncDetectedFeedback.BoolValue) + return BaseDmTx401.eSourceSelection.DisplayPort; + else if (Tx.CvbsInput.SyncDetectedFeedback.BoolValue) + return BaseDmTx401.eSourceSelection.Composite; + else + return BaseDmTx401.eSourceSelection.Disabled; + } + } + } + + public RoutingPortCollection InputPorts + { + get + { + return new RoutingPortCollection + { + HdmiIn, + DisplayPortIn, + VgaIn, + CompositeIn, + AnyVideoInput + }; + } + } + + public RoutingPortCollection OutputPorts + { + get + { + return new RoutingPortCollection { DmOut }; + } + } + + /// + /// + /// + /// + /// + /// + public DmTx401CController(string key, string name, DmTx401C tx) + : base(key, name, tx) + { + Tx = tx; + + HdmiIn = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, eVst.HDMI, this, + VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInput)); + DisplayPortIn = new RoutingInputPortWithVideoStatuses(DmPortName.DisplayPortIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, eVst.DisplayPort, this, + VideoStatusHelper.GetDisplayPortInputStatusFuncs(tx.DisplayPortInput)); + VgaIn = new RoutingInputPortWithVideoStatuses(DmPortName.VgaIn, + eRoutingSignalType.Video, eRoutingPortConnectionType.Vga, eVst.VGA, this, + VideoStatusHelper.GetVgaInputStatusFuncs(tx.VgaInput)); + CompositeIn = new RoutingInputPortWithVideoStatuses(DmPortName.CompositeIn, + eRoutingSignalType.Video, eRoutingPortConnectionType.Composite, eVst.Composite, this, + VideoStatusHelper.GetVgaInputStatusFuncs(tx.VgaInput)); + + Tx.BaseEvent += Tx_BaseEvent; + + ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", + () => ActualVideoInput.ToString()); + + VideoSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.VideoSourceFeedback; + }); + AudioSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.AudioSourceFeedback; + }); + + HdmiInHdcpCapabilityFeedback = new IntFeedback("HdmiInHdcpCapability", () => + { + if (tx.HdmiInput.HdpcSupportOnFeedback.BoolValue) + return 1; + else + return 0; + }); + + HdcpSupportCapability = eHdcpCapabilityType.HdcpAutoSupport; + + var combinedFuncs = new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => + (ActualVideoInput == eVst.HDMI + && tx.HdmiInput.VideoAttributes.HdcpActiveFeedback.BoolValue) + || (ActualVideoInput == eVst.DisplayPort + && tx.DisplayPortInput.VideoAttributes.HdcpActiveFeedback.BoolValue), + + HdcpStateFeedbackFunc = () => + { + if (ActualVideoInput == eVst.HDMI) + return tx.HdmiInput.VideoAttributes.HdcpStateFeedback.ToString(); + if (ActualVideoInput == eVst.DisplayPort) + return tx.DisplayPortInput.VideoAttributes.HdcpStateFeedback.ToString(); + return ""; + }, + + VideoResolutionFeedbackFunc = () => + { + if (ActualVideoInput == eVst.HDMI) + return tx.HdmiInput.VideoAttributes.GetVideoResolutionString(); + if (ActualVideoInput == eVst.DisplayPort) + return tx.DisplayPortInput.VideoAttributes.GetVideoResolutionString(); + if (ActualVideoInput == eVst.VGA) + return tx.VgaInput.VideoAttributes.GetVideoResolutionString(); + if (ActualVideoInput == eVst.Composite) + return tx.CvbsInput.VideoAttributes.GetVideoResolutionString(); + return ""; + }, + VideoSyncFeedbackFunc = () => + (ActualVideoInput == eVst.HDMI + && tx.HdmiInput.SyncDetectedFeedback.BoolValue) + || (ActualVideoInput == eVst.DisplayPort + && tx.DisplayPortInput.SyncDetectedFeedback.BoolValue) + || (ActualVideoInput == eVst.VGA + && tx.VgaInput.SyncDetectedFeedback.BoolValue) + || (ActualVideoInput == eVst.Composite + && tx.CvbsInput.SyncDetectedFeedback.BoolValue) + }; + + AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.None, 0, this, combinedFuncs); + + DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, null, this); + + AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, + AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, + AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, + AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiInHdcpCapabilityFeedback); + + // Set Ports for CEC + DisplayPortIn.Port = Tx.DisplayPortInput; + HdmiIn.Port = Tx.HdmiInput; + DmOut.Port = Tx.DmOutput; + } + + public override bool CustomActivate() + { + // Link up all of these damned events to the various RoutingPorts via a helper handler + Tx.HdmiInput.InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn, a.EventId); + Tx.HdmiInput.VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn, a.EventId); + + Tx.DisplayPortInput.InputStreamChange += (o, a) => FowardInputStreamChange(DisplayPortIn, a.EventId); + Tx.DisplayPortInput.VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(DisplayPortIn, a.EventId); + + Tx.VgaInput.InputStreamChange += (o, a) => FowardInputStreamChange(VgaIn, a.EventId); + Tx.VgaInput.VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(VgaIn, a.EventId); + + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) + { + Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); + + switch (input) + { + case 0: + { + ExecuteSwitch(eVst.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(DisplayPortIn.Selector, null, type); + break; + } + case 2: + { + ExecuteSwitch(HdmiIn.Selector, null, type); + break; + } + case 3: + { + ExecuteSwitch(VgaIn.Selector, null, type); + break; + } + case 4: + { + ExecuteSwitch(CompositeIn.Selector, null, type); + break; + } + case 5: + { + ExecuteSwitch(eVst.Disabled, null, type); + break; + } + } + + } + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + if ((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) + Tx.VideoSource = (eVst)inputSelector; + if ((signalType | eRoutingSignalType.Audio) == eRoutingSignalType.Audio) + Tx.AudioSource = (eVst)inputSelector; + } + + void Tx_BaseEvent(GenericBase device, BaseEventArgs args) + { + var id = args.EventId; + if (id == EndpointTransmitterBase.VideoSourceFeedbackEventId) + { + Debug.Console(2, this, " Video Source: {0}", Tx.VideoSourceFeedback); + VideoSourceNumericFeedback.FireUpdate(); + ActiveVideoInputFeedback.FireUpdate(); + } + + // ------------------------------ incomplete ----------------------------------------- + else if (id == EndpointTransmitterBase.AudioSourceFeedbackEventId) + { + Debug.Console(2, this, " Audio Source: {0}", Tx.AudioSourceFeedback); + AudioSourceNumericFeedback.FireUpdate(); + } + } + + /// + /// Relays the input stream change to the appropriate RoutingInputPort. + /// + void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + if (eventId == EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) + { + inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); + } + } + + /// + /// Relays the VideoAttributes change to a RoutingInputPort + /// + void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds + //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", + // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); + switch (eventId) + { + case VideoAttributeEventIds.HdcpActiveFeedbackEventId: + inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HdcpStateFeedbackEventId: + inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: + case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + } + } + + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Tx.ComPorts; } } + public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4k100Controller.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4k100Controller.cs new file mode 100644 index 00000000..5d3c7354 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4k100Controller.cs @@ -0,0 +1,105 @@ +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; +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM +{ + using eVst = Crestron.SimplSharpPro.DeviceSupport.eX02VideoSourceType; + using eAst = Crestron.SimplSharpPro.DeviceSupport.eX02AudioSourceType; + + public class DmTx4k100Controller : BasicDmTxControllerBase, IRoutingInputsOutputs, IHasFeedback, + IIROutputPorts, IComPorts, ICec + { + public DmTx4K100C1G Tx { get; private set; } + + public RoutingInputPort HdmiIn { get; private set; } + public RoutingOutputPort DmOut { get; private set; } + + //public IntFeedback VideoSourceNumericFeedback { get; protected set; } + //public IntFeedback AudioSourceNumericFeedback { get; protected set; } + //public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; } + //public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; } + + //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } + //public override ushort HdcpSupportCapability { get; protected set; } + + /// + /// Helps get the "real" inputs, including when in Auto + /// + public Crestron.SimplSharpPro.DeviceSupport.eX02VideoSourceType ActualActiveVideoInput + { + get + { + return eVst.Hdmi1; + } + } + public RoutingPortCollection InputPorts + { + get + { + return new RoutingPortCollection + { + HdmiIn + }; + } + } + public RoutingPortCollection OutputPorts + { + get + { + return new RoutingPortCollection { DmOut }; + } + } + public DmTx4k100Controller(string key, string name, DmTx4K100C1G tx) + : base(key, name, tx) + { + Tx = tx; + + HdmiIn = new RoutingInputPort(DmPortName.HdmiIn1, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, eVst.Hdmi1, this); + + DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, null, this); + + // Set Ports for CEC + HdmiIn.Port = Tx; + } + + + + public override bool CustomActivate() + { + // Link up all of these damned events to the various RoutingPorts via a helper handler + + + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Tx.ComPorts; } } + public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } + #endregion + + #region ICec Members + public Cec StreamCec { get { return Tx.StreamCec; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4k202CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4k202CController.cs new file mode 100644 index 00000000..b8061ad9 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4k202CController.cs @@ -0,0 +1,348 @@ +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; +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM +{ + using eVst = Crestron.SimplSharpPro.DeviceSupport.eX02VideoSourceType; + using eAst = Crestron.SimplSharpPro.DeviceSupport.eX02AudioSourceType; + + public class DmTx4k202CController : DmTxControllerBase, ITxRouting, IHasFeedback, + IIROutputPorts, IComPorts + { + public DmTx4kX02CBase Tx { get; private set; } + + public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; } + public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; } + public RoutingOutputPort DmOut { get; private set; } + public RoutingOutputPort HdmiLoopOut { get; private set; } + + public override StringFeedback ActiveVideoInputFeedback { get; protected set; } + public IntFeedback VideoSourceNumericFeedback { get; protected set; } + public IntFeedback AudioSourceNumericFeedback { get; protected set; } + public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; } + public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; } + + //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } + //public override ushort HdcpSupportCapability { get; protected set; } + + /// + /// Helps get the "real" inputs, including when in Auto + /// + public Crestron.SimplSharpPro.DeviceSupport.eX02VideoSourceType ActualActiveVideoInput + { + get + { + if (Tx.VideoSourceFeedback != eVst.Auto) + return Tx.VideoSourceFeedback; + else // auto + { + if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi1; + else if (Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi2; + else + return eVst.AllDisabled; + } + } + } + public RoutingPortCollection InputPorts + { + get + { + return new RoutingPortCollection + { + HdmiIn1, + HdmiIn2, + AnyVideoInput + }; + } + } + public RoutingPortCollection OutputPorts + { + get + { + return new RoutingPortCollection { DmOut, HdmiLoopOut }; + } + } + public DmTx4k202CController(string key, string name, DmTx4kX02CBase tx) + : base(key, name, tx) + { + Tx = tx; + + HdmiIn1 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn1, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, eVst.Hdmi1, this, + VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInputs[1])); + HdmiIn2 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn2, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, eVst.Hdmi2, this, + VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInputs[2])); + ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", + () => ActualActiveVideoInput.ToString()); + + + + Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent; + Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent; + Tx.BaseEvent += Tx_BaseEvent; + + VideoSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.VideoSourceFeedback; + }); + AudioSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.AudioSourceFeedback; + }); + + HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => + { + return (int)tx.HdmiInputs[1].HdcpCapabilityFeedback; + }); + + HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => + { + return (int)tx.HdmiInputs[2].HdcpCapabilityFeedback; + }); + + HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support; + + var combinedFuncs = new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue), + + HdcpStateFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString(); + if (ActualActiveVideoInput == eVst.Hdmi2) + return tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString(); + return ""; + }, + + VideoResolutionFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString(); + if (ActualActiveVideoInput == eVst.Hdmi2) + return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString(); + return ""; + }, + VideoSyncFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + + }; + + AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.None, 0, this, combinedFuncs); + + DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, null, this); + HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + + AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, + AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, + AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, + AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback); + + // Set Ports for CEC + HdmiIn1.Port = Tx.HdmiInputs[1]; + HdmiIn2.Port = Tx.HdmiInputs[2]; + HdmiLoopOut.Port = Tx.HdmiOutput; + DmOut.Port = Tx.DmOutput; + } + + + + public override bool CustomActivate() + { + // Link up all of these damned events to the various RoutingPorts via a helper handler + Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId); + Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId); + + Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId); + Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId); + + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) + { + Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); + + if (type == eRoutingSignalType.Video) + { + switch (input) + { + case 0: + { + ExecuteSwitch(eVst.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(HdmiIn1.Selector, null, type); + break; + } + case 2: + { + ExecuteSwitch(HdmiIn2.Selector, null, type); + break; + } + case 3: + { + ExecuteSwitch(eVst.AllDisabled, null, type); + break; + } + } + } + else if (type == eRoutingSignalType.Audio) + { + switch (input) + { + case 0: + { + ExecuteSwitch(eAst.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(eAst.Hdmi1, null, type); + break; + } + case 2: + { + ExecuteSwitch(eAst.Hdmi2, null, type); + break; + } + case 3: + { + ExecuteSwitch(eAst.AllDisabled, null, type); + break; + } + } + } + } + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + if ((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) + Tx.VideoSource = (eVst)inputSelector; + if ((signalType | eRoutingSignalType.Audio) == eRoutingSignalType.Audio) + Tx.AudioSource = (eAst)inputSelector; + } + + void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) + { + Debug.Console(2, "{0} event {1} stream {2}", this.Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); + + if (args.EventId == EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId) + { + if (inputStream == Tx.HdmiInputs[1]) + HdmiIn1HdcpCapabilityFeedback.FireUpdate(); + else if (inputStream == Tx.HdmiInputs[2]) + HdmiIn2HdcpCapabilityFeedback.FireUpdate(); + } + else if (args.EventId == EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId) + { + if (inputStream == Tx.HdmiInputs[1]) + HdmiIn1HdcpCapabilityFeedback.FireUpdate(); + else if (inputStream == Tx.HdmiInputs[2]) + HdmiIn2HdcpCapabilityFeedback.FireUpdate(); + } + } + + void Tx_BaseEvent(GenericBase device, BaseEventArgs args) + { + var id = args.EventId; + if (id == EndpointTransmitterBase.VideoSourceFeedbackEventId) + { + Debug.Console(2, this, " Video Source: {0}", Tx.VideoSourceFeedback); + VideoSourceNumericFeedback.FireUpdate(); + ActiveVideoInputFeedback.FireUpdate(); + } + + // ------------------------------ incomplete ----------------------------------------- + else if (id == EndpointTransmitterBase.AudioSourceFeedbackEventId) + { + Debug.Console(2, this, " Audio Source: {0}", Tx.AudioSourceFeedback); + AudioSourceNumericFeedback.FireUpdate(); + } + } + + /// + /// Relays the input stream change to the appropriate RoutingInputPort. + /// + void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + if (eventId == EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) + { + inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); + } + } + + /// + /// Relays the VideoAttributes change to a RoutingInputPort + /// + void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds + //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", + // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); + switch (eventId) + { + case VideoAttributeEventIds.HdcpActiveFeedbackEventId: + inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HdcpStateFeedbackEventId: + inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: + case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + } + } + + + + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Tx.ComPorts; } } + public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4k302CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4k302CController.cs new file mode 100644 index 00000000..b086e430 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4k302CController.cs @@ -0,0 +1,369 @@ +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; +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM +{ + using eVst = Crestron.SimplSharpPro.DeviceSupport.eX02VideoSourceType; + using eAst = Crestron.SimplSharpPro.DeviceSupport.eX02AudioSourceType; + + public class DmTx4k302CController : DmTxControllerBase, ITxRouting, IHasFeedback, + IIROutputPorts, IComPorts + { + public DmTx4k302C Tx { get; private set; } + + public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; } + public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; } + public RoutingInputPortWithVideoStatuses VgaIn { get; private set; } + public RoutingOutputPort DmOut { get; private set; } + public RoutingOutputPort HdmiLoopOut { get; private set; } + + public override StringFeedback ActiveVideoInputFeedback { get; protected set; } + public IntFeedback VideoSourceNumericFeedback { get; protected set; } + public IntFeedback AudioSourceNumericFeedback { get; protected set; } + public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; } + public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; } + + //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } + //public override ushort HdcpSupportCapability { get; protected set; } + + /// + /// Helps get the "real" inputs, including when in Auto + /// + public Crestron.SimplSharpPro.DeviceSupport.eX02VideoSourceType ActualActiveVideoInput + { + get + { + if (Tx.VideoSourceFeedback != eVst.Auto) + return Tx.VideoSourceFeedback; + else // auto + { + if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi1; + else if (Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi2; + else if (Tx.VgaInput.SyncDetectedFeedback.BoolValue) + return eVst.Vga; + else + return eVst.AllDisabled; + } + } + } + public RoutingPortCollection InputPorts + { + get + { + return new RoutingPortCollection + { + HdmiIn1, + HdmiIn2, + VgaIn, + AnyVideoInput + }; + } + } + public RoutingPortCollection OutputPorts + { + get + { + return new RoutingPortCollection { DmOut, HdmiLoopOut }; + } + } + public DmTx4k302CController(string key, string name, DmTx4k302C tx) + : base(key, name, tx) + { + Tx = tx; + + HdmiIn1 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn1, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, eVst.Hdmi1, this, + VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInputs[1])); + HdmiIn2 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn2, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, eVst.Hdmi2, this, + VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInputs[2])); + VgaIn = new RoutingInputPortWithVideoStatuses(DmPortName.VgaIn, + eRoutingSignalType.Video, eRoutingPortConnectionType.Vga, eVst.Vga, this, + VideoStatusHelper.GetVgaInputStatusFuncs(tx.VgaInput)); + ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", + () => ActualActiveVideoInput.ToString()); + + Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent; + Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent; + Tx.BaseEvent += Tx_BaseEvent; + + VideoSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.VideoSourceFeedback; + }); + AudioSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.AudioSourceFeedback; + }); + + HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => + { + return (int)tx.HdmiInputs[1].HdcpCapabilityFeedback; + }); + + HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => + { + return (int)tx.HdmiInputs[2].HdcpCapabilityFeedback; + }); + + HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support; + + + var combinedFuncs = new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue), + + HdcpStateFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString(); + if (ActualActiveVideoInput == eVst.Hdmi2) + return tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString(); + return ""; + }, + + VideoResolutionFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString(); + if (ActualActiveVideoInput == eVst.Hdmi2) + return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString(); + if (ActualActiveVideoInput == eVst.Vga) + return tx.VgaInput.VideoAttributes.GetVideoResolutionString(); + return ""; + }, + VideoSyncFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Vga + && tx.VgaInput.SyncDetectedFeedback.BoolValue) + + }; + + AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.None, 0, this, combinedFuncs); + + DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, null, this); + HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + + AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, + AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, + AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, + AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback); + + // Set Ports for CEC + HdmiIn1.Port = Tx.HdmiInputs[1]; + HdmiIn2.Port = Tx.HdmiInputs[2]; + HdmiLoopOut.Port = Tx.HdmiOutput; + DmOut.Port = Tx.DmOutput; + } + + + + public override bool CustomActivate() + { + // Link up all of these damned events to the various RoutingPorts via a helper handler + Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId); + Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId); + + Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId); + Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId); + + Tx.VgaInput.InputStreamChange += (o, a) => FowardInputStreamChange(VgaIn, a.EventId); + Tx.VgaInput.VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(VgaIn, a.EventId); + + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) + { + Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); + + if (type == eRoutingSignalType.Video) + { + switch (input) + { + case 0: + { + ExecuteSwitch(eVst.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(HdmiIn1.Selector, null, type); + break; + } + case 2: + { + ExecuteSwitch(HdmiIn2.Selector, null, type); + break; + } + case 3: + { + ExecuteSwitch(VgaIn.Selector, null, type); + break; + } + case 4: + { + ExecuteSwitch(eVst.AllDisabled, null, type); + break; + } + } + } + else if (type == eRoutingSignalType.Audio) + { + switch (input) + { + case 0: + { + ExecuteSwitch(eAst.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(eAst.Hdmi1, null, type); + break; + } + case 2: + { + ExecuteSwitch(eAst.Hdmi2, null, type); + break; + } + case 3: + { + ExecuteSwitch(eAst.AudioIn, null, type); + break; + } + case 4: + { + ExecuteSwitch(eAst.AllDisabled, null, type); + break; + } + } + } + } + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + if ((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) + Tx.VideoSource = (eVst)inputSelector; + if ((signalType | eRoutingSignalType.Audio) == eRoutingSignalType.Audio) + Tx.AudioSource = (eAst)inputSelector; + } + + void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) + { + Debug.Console(2, "{0} event {1} stream {2}", this.Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); + + if (args.EventId == EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId) + { + if (inputStream == Tx.HdmiInputs[1]) + HdmiIn1HdcpCapabilityFeedback.FireUpdate(); + else if (inputStream == Tx.HdmiInputs[2]) + HdmiIn2HdcpCapabilityFeedback.FireUpdate(); + } + else if (args.EventId == EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId) + { + if (inputStream == Tx.HdmiInputs[1]) + HdmiIn1HdcpCapabilityFeedback.FireUpdate(); + else if (inputStream == Tx.HdmiInputs[2]) + HdmiIn2HdcpCapabilityFeedback.FireUpdate(); + } + } + + void Tx_BaseEvent(GenericBase device, BaseEventArgs args) + { + var id = args.EventId; + if (id == EndpointTransmitterBase.VideoSourceFeedbackEventId) + { + Debug.Console(2, this, " Video Source: {0}", Tx.VideoSourceFeedback); + VideoSourceNumericFeedback.FireUpdate(); + ActiveVideoInputFeedback.FireUpdate(); + } + + // ------------------------------ incomplete ----------------------------------------- + else if (id == EndpointTransmitterBase.AudioSourceFeedbackEventId) + { + Debug.Console(2, this, " Audio Source: {0}", Tx.AudioSourceFeedback); + AudioSourceNumericFeedback.FireUpdate(); + } + } + + /// + /// Relays the input stream change to the appropriate RoutingInputPort. + /// + void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + if (eventId == EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) + { + inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); + } + } + + /// + /// Relays the VideoAttributes change to a RoutingInputPort + /// + void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds + //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", + // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); + switch (eventId) + { + case VideoAttributeEventIds.HdcpActiveFeedbackEventId: + inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HdcpStateFeedbackEventId: + inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: + case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + } + } + + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Tx.ComPorts; } } + public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs new file mode 100644 index 00000000..26dcb603 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs @@ -0,0 +1,340 @@ +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; +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM +{ + using eVst = Crestron.SimplSharpPro.DeviceSupport.eX02VideoSourceType; + using eAst = Crestron.SimplSharpPro.DeviceSupport.eX02AudioSourceType; + + public class DmTx4kz302CController : DmTxControllerBase, ITxRouting, IHasFeedback, + IIROutputPorts, IComPorts + { + public DmTx4kz302C Tx { get; private set; } + + public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; } + public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; } + public RoutingInputPortWithVideoStatuses DisplayPortIn { get; private set; } + public RoutingOutputPort DmOut { get; private set; } + public RoutingOutputPort HdmiLoopOut { get; private set; } + + public override StringFeedback ActiveVideoInputFeedback { get; protected set; } + public IntFeedback VideoSourceNumericFeedback { get; protected set; } + public IntFeedback AudioSourceNumericFeedback { get; protected set; } + public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; } + public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; } + + //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } + //public override ushort HdcpSupportCapability { get; protected set; } + + /// + /// Helps get the "real" inputs, including when in Auto + /// + public Crestron.SimplSharpPro.DeviceSupport.eX02VideoSourceType ActualActiveVideoInput + { + get + { + if (Tx.VideoSourceFeedback != eVst.Auto) + return Tx.VideoSourceFeedback; + else // auto + { + if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi1; + else if (Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi2; + else if (Tx.DisplayPortInput.SyncDetectedFeedback.BoolValue) + return eVst.Vga; + else + return eVst.AllDisabled; + } + } + } + public RoutingPortCollection InputPorts + { + get + { + return new RoutingPortCollection + { + HdmiIn1, + HdmiIn2, + DisplayPortIn, + AnyVideoInput + }; + } + } + public RoutingPortCollection OutputPorts + { + get + { + return new RoutingPortCollection { DmOut, HdmiLoopOut }; + } + } + public DmTx4kz302CController(string key, string name, DmTx4kz302C tx) + : base(key, name, tx) + { + Tx = tx; + + HdmiIn1 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn1, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, eVst.Hdmi1, this, + VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInputs[1])); + HdmiIn2 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn2, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, eVst.Hdmi2, this, + VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInputs[2])); + DisplayPortIn = new RoutingInputPortWithVideoStatuses(DmPortName.VgaIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DisplayPort, eVst.DisplayPort, this, + VideoStatusHelper.GetDisplayPortInputStatusFuncs(tx.DisplayPortInput)); + ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", + () => ActualActiveVideoInput.ToString()); + + Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent; + Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent; + Tx.BaseEvent += Tx_BaseEvent; + + VideoSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.VideoSourceFeedback; + }); + AudioSourceNumericFeedback = new IntFeedback(() => + { + return (int)Tx.AudioSourceFeedback; + }); + + HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => + { + return (int)tx.HdmiInputs[1].HdcpCapabilityFeedback; + }); + + HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => + { + return (int)tx.HdmiInputs[2].HdcpCapabilityFeedback; + }); + + HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support; + + + var combinedFuncs = new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue), + + HdcpStateFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString(); + if (ActualActiveVideoInput == eVst.Hdmi2) + return tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString(); + return ""; + }, + + VideoResolutionFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString(); + if (ActualActiveVideoInput == eVst.Hdmi2) + return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString(); + if (ActualActiveVideoInput == eVst.Vga) + return tx.DisplayPortInput.VideoAttributes.GetVideoResolutionString(); + return ""; + }, + VideoSyncFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Vga + && tx.DisplayPortInput.SyncDetectedFeedback.BoolValue) + + }; + + AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, + eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.None, 0, this, combinedFuncs); + + DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DmCat, null, this); + HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + + AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, + AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, + AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, + AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback); + + // Set Ports for CEC + HdmiIn1.Port = Tx.HdmiInputs[1]; + HdmiIn2.Port = Tx.HdmiInputs[2]; + HdmiLoopOut.Port = Tx.HdmiOutput; + DmOut.Port = Tx.DmOutput; + } + + + + public override bool CustomActivate() + { + // Link up all of these damned events to the various RoutingPorts via a helper handler + Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId); + Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId); + + Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId); + Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId); + + Tx.DisplayPortInput.InputStreamChange += (o, a) => FowardInputStreamChange(DisplayPortIn, a.EventId); + Tx.DisplayPortInput.VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(DisplayPortIn, a.EventId); + + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) + { + Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); + + switch (input) + { + case 0: + { + ExecuteSwitch(eVst.Auto, null, eRoutingSignalType.AudioVideo); + break; + } + case 1: + { + ExecuteSwitch(HdmiIn1.Selector, null, eRoutingSignalType.AudioVideo); + break; + } + case 2: + { + ExecuteSwitch(HdmiIn2.Selector, null, eRoutingSignalType.AudioVideo); + break; + } + case 3: + { + ExecuteSwitch(DisplayPortIn.Selector, null, eRoutingSignalType.AudioVideo); + break; + } + case 4: + { + ExecuteSwitch(eVst.AllDisabled, null, eRoutingSignalType.AudioVideo); + break; + } + } + + + } + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + if ((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) + Tx.VideoSource = (eVst)inputSelector; + + // NOTE: It's possible that this particular TX model may not like the AudioSource property being set. + // The SIMPL definition only shows a single analog for AudioVideo Source + if ((signalType | eRoutingSignalType.Audio) == eRoutingSignalType.Audio) + Tx.AudioSource = (eAst)inputSelector; + } + + void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) + { + Debug.Console(2, "{0} event {1} stream {2}", this.Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); + + if (args.EventId == EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId) + { + if (inputStream == Tx.HdmiInputs[1]) + HdmiIn1HdcpCapabilityFeedback.FireUpdate(); + else if (inputStream == Tx.HdmiInputs[2]) + HdmiIn2HdcpCapabilityFeedback.FireUpdate(); + } + else if (args.EventId == EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId) + { + if (inputStream == Tx.HdmiInputs[1]) + HdmiIn1HdcpCapabilityFeedback.FireUpdate(); + else if (inputStream == Tx.HdmiInputs[2]) + HdmiIn2HdcpCapabilityFeedback.FireUpdate(); + } + } + + void Tx_BaseEvent(GenericBase device, BaseEventArgs args) + { + var id = args.EventId; + if (id == EndpointTransmitterBase.VideoSourceFeedbackEventId) + { + Debug.Console(2, this, " Video Source: {0}", Tx.VideoSourceFeedback); + VideoSourceNumericFeedback.FireUpdate(); + ActiveVideoInputFeedback.FireUpdate(); + } + + // ------------------------------ incomplete ----------------------------------------- + else if (id == EndpointTransmitterBase.AudioSourceFeedbackEventId) + { + Debug.Console(2, this, " Audio Source: {0}", Tx.AudioSourceFeedback); + AudioSourceNumericFeedback.FireUpdate(); + } + } + + /// + /// Relays the input stream change to the appropriate RoutingInputPort. + /// + void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + if (eventId == EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) + { + inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); + } + } + + /// + /// Relays the VideoAttributes change to a RoutingInputPort + /// + void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds + //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", + // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); + switch (eventId) + { + case VideoAttributeEventIds.HdcpActiveFeedbackEventId: + inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HdcpStateFeedbackEventId: + inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: + case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + } + } + + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Tx.ComPorts; } } + public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs new file mode 100644 index 00000000..48507151 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.DM.Config; + +namespace PepperDash.Essentials.DM +{ + public class DmTxHelper + { + /// + /// A factory method for various DmTxControllers + /// + /// + /// + /// + /// + public static BasicDmTxControllerBase GetDmTxController(string key, string name, string typeName, DmTxPropertiesConfig props) + { + // switch on type name... later... + + typeName = typeName.ToLower(); + //uint ipid = Convert.ToUInt16(props.Id, 16); + var ipid = props.Control.IpIdInt; + var pKey = props.ParentDeviceKey.ToLower(); + + if (pKey == "processor") + { + // Catch constructor failures, mainly dues to IPID + try + { + if(typeName.StartsWith("dmTx200")) + return new DmTx200Controller(key, name, new DmTx200C2G(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmtx201")) + return new DmTx201XController(key, name, new DmTx201S(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmtx4k202")) + return new DmTx4k202CController(key, name, new DmTx4k202C(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmtx4kz202")) + return new DmTx4k202CController(key, name, new DmTx4kz202C(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmtx4k302")) + return new DmTx4k302CController(key, name, new DmTx4k302C(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmtx4kz302")) + return new DmTx4kz302CController(key, name, new DmTx4kz302C(ipid, Global.ControlSystem)); + if (typeName.StartsWith("dmtx401")) + return new DmTx401CController(key, name, new DmTx401C(ipid, Global.ControlSystem)); + Debug.Console(0, "{1} WARNING: Cannot create DM-TX of type: '{0}'", typeName, key); + } + catch (Exception e) + { + Debug.Console(0, "[{0}] WARNING: Cannot create DM-TX device: {1}", key, e.Message); + } + } + else + { + var parentDev = DeviceManager.GetDeviceForKey(pKey); + if (!(parentDev is DmChassisController)) + { + Debug.Console(0, "Cannot create DM device '{0}'. '{1}' is not a DM Chassis.", + key, pKey); + return null; + } + + // Get the Crestron chassis and link stuff up + var switchDev = parentDev as DmChassisController; + var chassis = switchDev.Chassis; + + var num = props.ParentInputNumber; + if (num <= 0 || num > chassis.NumberOfInputs) + { + Debug.Console(0, "Cannot create DM device '{0}'. Input number '{1}' is out of range", + key, num); + return null; + } + else + { + var controller = (parentDev as DmChassisController); + controller.TxDictionary.Add(num, key); + } + + // Catch constructor failures, mainly dues to IPID + try + { + // Must use different constructor for CPU3 chassis types. No IPID + if (chassis is DmMd8x8Cpu3 || chassis is DmMd16x16Cpu3 || + chassis is DmMd32x32Cpu3 || chassis is DmMd8x8Cpu3rps || + chassis is DmMd16x16Cpu3rps || chassis is DmMd32x32Cpu3rps) + { + if (typeName.StartsWith("dmTx200")) + return new DmTx200Controller(key, name, new DmTx200C2G(chassis.Inputs[num])); + if (typeName.StartsWith("dmtx201")) + return new DmTx201XController(key, name, new DmTx201C(chassis.Inputs[num])); + if (typeName.StartsWith("dmtx4k100")) + return new DmTx4k100Controller(key, name, new DmTx4K100C1G(chassis.Inputs[num])); + if (typeName.StartsWith("dmtx4k202")) + return new DmTx4k202CController(key, name, new DmTx4k202C(chassis.Inputs[num])); + if (typeName.StartsWith("dmtx4kz202")) + return new DmTx4k202CController(key, name, new DmTx4kz202C(chassis.Inputs[num])); + if (typeName.StartsWith("dmtx4k302")) + return new DmTx4k302CController(key, name, new DmTx4k302C(chassis.Inputs[num])); + if (typeName.StartsWith("dmtx4kz302")) + return new DmTx4kz302CController(key, name, new DmTx4kz302C(chassis.Inputs[num])); + if (typeName.StartsWith("dmtx401")) + return new DmTx401CController(key, name, new DmTx401C(chassis.Inputs[num])); + } + else + { + if (typeName.StartsWith("dmTx200")) + return new DmTx200Controller(key, name, new DmTx200C2G(ipid, chassis.Inputs[num])); + if (typeName.StartsWith("dmtx201")) + return new DmTx201XController(key, name, new DmTx201C(ipid, chassis.Inputs[num])); + if (typeName.StartsWith("dmtx4k100")) + return new DmTx4k100Controller(key, name, new DmTx4K100C1G(ipid, chassis.Inputs[num])); + if (typeName.StartsWith("dmtx4k202")) + return new DmTx4k202CController(key, name, new DmTx4k202C(ipid, chassis.Inputs[num])); + if (typeName.StartsWith("dmtx4kz202")) + return new DmTx4k202CController(key, name, new DmTx4kz202C(ipid, chassis.Inputs[num])); + if (typeName.StartsWith("dmtx4k302")) + return new DmTx4k302CController(key, name, new DmTx4k302C(ipid, chassis.Inputs[num])); + if (typeName.StartsWith("dmtx4kz302")) + return new DmTx4kz302CController(key, name, new DmTx4kz302C(ipid, chassis.Inputs[num])); + if (typeName.StartsWith("dmtx401")) + return new DmTx401CController(key, name, new DmTx401C(ipid, chassis.Inputs[num])); + } + } + catch (Exception e) + { + Debug.Console(0, "[{0}] WARNING: Cannot create DM-TX device: {1}", key, e.Message); + } + } + + return null; + } + } + + public abstract class BasicDmTxControllerBase : CrestronGenericBaseDevice + { + public BasicDmTxControllerBase(string key, string name, GenericBase hardware) + : base(key, name, hardware) + { + + } + } + + /// + /// + /// + public abstract class DmTxControllerBase : BasicDmTxControllerBase + { + public virtual void SetPortHdcpCapability(eHdcpCapabilityType hdcpMode, uint port) { } + public virtual eHdcpCapabilityType HdcpSupportCapability { get; protected set; } + public abstract StringFeedback ActiveVideoInputFeedback { get; protected set; } + public RoutingInputPortWithVideoStatuses AnyVideoInput { get; protected set; } + + public DmTxControllerBase(string key, string name, EndpointTransmitterBase hardware) + : base(key, name, hardware) + { + // if wired to a chassis, skip registration step in base class + if (hardware.DMInput != null) + { + this.PreventRegistration = true; + } + AddToFeedbackList(ActiveVideoInputFeedback); + } + } + //public enum ePdtHdcpSupport + //{ + // HdcpOff = 0, + // Hdcp1 = 1, + // Hdcp2 = 2, + // Hdcp2_2= 3, + // Auto = 99 + //} +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Essentials_DM.csproj b/essentials-framework/Essentials DM/Essentials_DM/Essentials_DM.csproj new file mode 100644 index 00000000..e6dad5cb --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Essentials_DM.csproj @@ -0,0 +1,160 @@ + + + Release + AnyCPU + 9.0.30729 + 2.0 + {9199CE8A-0C9F-4952-8672-3EED798B284F} + Library + Properties + PepperDash.Essentials.DM + PepperDash_Essentials_DM + {0B4745B0-194B-4BB6-8E21-E9057CA92300};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + WindowsCE + E2BECB1F-8C8C-41ba-B736-9BE7D946A398 + 5.0 + SmartDeviceProject1 + v3.5 + Windows CE + + + + + .allowedReferenceRelatedFileExtensions + true + full + false + bin\ + DEBUG;TRACE; + prompt + 4 + 512 + true + true + off + + + .allowedReferenceRelatedFileExtensions + none + true + bin\ + prompt + 4 + 512 + true + true + off + + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll + + + False + ..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll + + + + False + ..\..\references\PepperDash_Core.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + False + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + False + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5} + PepperDash_Essentials_Core + + + + + + + + + rem S# Pro preparation will execute after these operations + + \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Extensions.cs b/essentials-framework/Essentials DM/Essentials_DM/Extensions.cs new file mode 100644 index 00000000..c1ccfc5d --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Extensions.cs @@ -0,0 +1,43 @@ +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.Cards; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + public static class VideoAttributesBasicExtensions + { + public static string GetVideoResolutionString(this VideoAttributesBasic va) + { + ushort h = va.HorizontalResolutionFeedback.UShortValue; + ushort v = va.VerticalResolutionFeedback.UShortValue; + ushort r = va.FramesPerSecondFeedback.UShortValue; + if (h == 0 || v == 0) + return "n/a"; + else + return string.Format("{0}x{1}@{2}Hz", h, v, r); + } + } + + public static class AdvEndpointHdmiOutputExtensions + { + public static string GetVideoResolutionString(this AdvEndpointHdmiOutput va) + { + ushort h = va.HorizontalResolutionFeedback.UShortValue; + ushort v = va.VerticalResolutionFeedback.UShortValue; + ushort r = va.FramesPerSecondFeedback.UShortValue; + if (h == 0 || v == 0) + return "n/a"; + else + return string.Format("{0}x{1}@{2}Hz", h, v, r); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/IDmHdmiInputExtensions.cs b/essentials-framework/Essentials DM/Essentials_DM/IDmHdmiInputExtensions.cs new file mode 100644 index 00000000..a014e976 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/IDmHdmiInputExtensions.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DM; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + public static class IBasicDmInputExtensions + { + public static VideoStatusFuncsWrapper GetVideoStatusFuncsWrapper(this IBasicDMInput input) + { + var va = (input as IVideoAttributesEnhanced).VideoAttributes; + return new VideoStatusFuncsWrapper + { + HasVideoStatusFunc = () => true, + HdcpActiveFeedbackFunc = () => va.HdcpActiveFeedback.BoolValue, + HdcpStateFeedbackFunc = () => va.HdcpStateFeedback.ToString(), + VideoResolutionFeedbackFunc = () => + { + //var h = va.HorizontalResolutionFeedback.UShortValue; + //var v = va.VerticalResolutionFeedback.UShortValue; + //if (h == 0 || v == 0) + // return "---"; + return va.GetVideoResolutionString(); // h + "x" + v; + }, + VideoSyncFeedbackFunc = () => input.SyncDetectedFeedback.BoolValue + }; + } + } + + + public static class IEndpointHdmiInputExtensions + { + public static VideoStatusFuncsWrapper GetVideoStatusFuncsWrapper(this Crestron.SimplSharpPro.DM.Endpoints.EndpointHdmiInput input) + { + var va = (input as IVideoAttributesEnhanced).VideoAttributes; + return new VideoStatusFuncsWrapper + { + HasVideoStatusFunc = () => true, + HdcpActiveFeedbackFunc = () => va.HdcpActiveFeedback.BoolValue, + HdcpStateFeedbackFunc = () => va.HdcpStateFeedback.ToString(), + VideoResolutionFeedbackFunc = () => + { + //var h = va.HorizontalResolutionFeedback.UShortValue; + //var v = va.VerticalResolutionFeedback.UShortValue; + //if (h == 0 || v == 0) + // return "---"; + return va.GetVideoResolutionString(); // h + "x" + v; + }, + VideoSyncFeedbackFunc = () => input.SyncDetectedFeedback.BoolValue + }; + } + } + + +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/MOVE IBasicVideoStatusFeedbacks.cs b/essentials-framework/Essentials DM/Essentials_DM/MOVE IBasicVideoStatusFeedbacks.cs new file mode 100644 index 00000000..26b950ce --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/MOVE IBasicVideoStatusFeedbacks.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + public interface IBasicVideoStatusFeedbacks + { + BoolFeedback HasVideoStatusFeedback { get; } + BoolFeedback HdcpActiveFeedback { get; } + StringFeedback HdcpStateFeedback { get; } + StringFeedback VideoResolutionFeedback { get; } + BoolFeedback VideoSyncFeedback { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Nvx/NvxDirectorController.cs b/essentials-framework/Essentials DM/Essentials_DM/Nvx/NvxDirectorController.cs new file mode 100644 index 00000000..fa9749c5 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Nvx/NvxDirectorController.cs @@ -0,0 +1,746 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; +//using Crestron.SimplSharpPro; +//using Crestron.SimplSharpPro.DM; +//using Crestron.SimplSharpPro.DM.Cards; +//using Crestron.SimplSharpPro.DM.Streaming; +//using Crestron.SimplSharpPro.DM.Endpoints; +//using Crestron.SimplSharpPro.DM.Endpoints.Receivers; + +//using PepperDash.Core; +//using PepperDash.Essentials.Core; +//using PepperDash.Essentials.DM.Cards; +//using PepperDash.Essentials.DM.Config; + +//namespace PepperDash.Essentials.DM +//{ +// /// +// /// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions +// /// +// /// +// public class NvxDirectorController : CrestronGenericBaseDevice, IRoutingInputsOutputs, IRouting, IHasFeedback//, ICardPortsDevice +// { +// public NvxDirectorController Chassis { get; private set; } + +// // Feedbacks for EssentialDM +// public Dictionary VideoOutputFeedbacks { get; private set; } +// public Dictionary AudioOutputFeedbacks { get; private set; } +// public Dictionary VideoInputSyncFeedbacks { get; private set; } +// public Dictionary InputEndpointOnlineFeedbacks { get; private set; } +// public Dictionary OutputEndpointOnlineFeedbacks { get; private set; } +// public Dictionary InputNameFeedbacks { get; private set; } +// public Dictionary OutputNameFeedbacks { get; private set; } +// public Dictionary OutputVideoRouteNameFeedbacks { get; private set; } +// public Dictionary OutputAudioRouteNameFeedbacks { get; private set; } + + +// // Need a couple Lists of generic Backplane ports +// public RoutingPortCollection InputPorts { get; private set; } +// public RoutingPortCollection OutputPorts { get; private set; } + +// public Dictionary TxDictionary { get; set; } +// public Dictionary RxDictionary { get; set; } + +// //public Dictionary InputCards { get; private set; } +// //public Dictionary OutputCards { get; private set; } + +// public Dictionary InputNames { get; set; } +// public Dictionary OutputNames { get; set; } +// public Dictionary VolumeControls { get; private set; } + +// public const int RouteOffTime = 500; +// Dictionary RouteOffTimers = new Dictionary(); + +// /// +// /// Factory method to create a new chassis controller from config data. Limited to 8x8 right now +// /// +// public static NvxDirectorController GetNvxDirectorController(string key, string name, +// string type, DMChassisPropertiesConfig properties) +// { +// try +// { +// type = type.ToLower(); +// uint ipid = properties.Control.IpIdInt; // Convert.ToUInt16(properties.Id, 16); +// NvxDirectorController controller = null; + + +// if (type == "dmmd8x8") +// { +// controller = new NvxDirectorController(key, name, new DmMd8x8(ipid, Global.ControlSystem)); + +// // add the cards and port names +// foreach (var kvp in properties.InputSlots) +// controller.AddInputCard(kvp.Value, kvp.Key); +// foreach (var kvp in properties.OutputSlots) { +// controller.AddOutputCard(kvp.Value, kvp.Key); + +// } + +// foreach (var kvp in properties.VolumeControls) +// { +// // get the card +// // check it for an audio-compatible type +// // make a something-something that will make it work +// // retire to mountain village +// var outNum = kvp.Key; +// var card = controller.Chassis.Outputs[outNum].Card; +// Audio.Output audio = null; +// if (card is DmcHdo) +// audio = (card as DmcHdo).Audio; +// else if (card is Dmc4kHdo) +// audio = (card as Dmc4kHdo).Audio; +// if (audio == null) +// continue; +// // wire up the audio to something here... +// controller.AddVolumeControl(outNum, audio); +// } + +// controller.InputNames = properties.InputNames; +// controller.OutputNames = properties.OutputNames; +// return controller; +// } +// else if (type == "dmmd16x16") { +// controller = new NvxDirectorController(key, name, new DmMd16x16(ipid, Global.ControlSystem)); + +// // add the cards and port names +// foreach (var kvp in properties.InputSlots) +// controller.AddInputCard(kvp.Value, kvp.Key); +// foreach (var kvp in properties.OutputSlots) { +// controller.AddOutputCard(kvp.Value, kvp.Key); + +// } + +// foreach (var kvp in properties.VolumeControls) { +// // get the card +// // check it for an audio-compatible type +// // make a something-something that will make it work +// // retire to mountain village +// var outNum = kvp.Key; +// var card = controller.Chassis.Outputs[outNum].Card; +// Audio.Output audio = null; +// if (card is DmcHdo) +// audio = (card as DmcHdo).Audio; +// else if (card is Dmc4kHdo) +// audio = (card as Dmc4kHdo).Audio; +// if (audio == null) +// continue; +// // wire up the audio to something here... +// controller.AddVolumeControl(outNum, audio); +// } + +// controller.InputNames = properties.InputNames; +// controller.OutputNames = properties.OutputNames; +// return controller; +// } +// } +// catch (System.Exception e) +// { +// Debug.Console(0, "Error creating DM chassis:\r{0}", e); +// } +// return null; +// } + + +// /// +// /// +// /// +// /// +// /// +// /// +// public NvxDirectorController(string key, string name, DmMDMnxn chassis) +// : base(key, name, chassis) +// { +// Chassis = chassis; +// InputPorts = new RoutingPortCollection(); +// OutputPorts = new RoutingPortCollection(); +// VolumeControls = new Dictionary(); +// TxDictionary = new Dictionary(); +// RxDictionary = new Dictionary(); +// IsOnline.OutputChange += new EventHandler(IsOnline_OutputChange); +// //IsOnline.OutputChange += new EventHandler(this.IsOnline_OutputChange); +// Chassis.DMInputChange += new DMInputEventHandler(Chassis_DMInputChange); +// //Chassis.DMSystemChange += new DMSystemEventHandler(Chassis_DMSystemChange); +// Chassis.DMOutputChange += new DMOutputEventHandler(Chassis_DMOutputChange); +// VideoOutputFeedbacks = new Dictionary(); +// AudioOutputFeedbacks = new Dictionary(); +// VideoInputSyncFeedbacks = new Dictionary(); +// InputNameFeedbacks = new Dictionary(); +// OutputNameFeedbacks = new Dictionary(); +// OutputVideoRouteNameFeedbacks = new Dictionary(); +// OutputAudioRouteNameFeedbacks = new Dictionary(); +// InputEndpointOnlineFeedbacks = new Dictionary(); +// OutputEndpointOnlineFeedbacks = new Dictionary(); + +// for (uint x = 1; x <= Chassis.NumberOfOutputs; x++) +// { +// var tempX = x; + +// VideoOutputFeedbacks[tempX] = new IntFeedback(() => { +// if (Chassis.Outputs[tempX].VideoOutFeedback != null) { return (ushort)Chassis.Outputs[tempX].VideoOutFeedback.Number;} +// else { return 0; }; +// }); +// AudioOutputFeedbacks[tempX] = new IntFeedback(() => { +// if (Chassis.Outputs[tempX].AudioOutFeedback != null) { return (ushort)Chassis.Outputs[tempX].AudioOutFeedback.Number; } +// else { return 0; }; +// }); +// VideoInputSyncFeedbacks[tempX] = new BoolFeedback(() => { +// return Chassis.Inputs[tempX].VideoDetectedFeedback.BoolValue; +// }); +// InputNameFeedbacks[tempX] = new StringFeedback(() => { +// if (Chassis.Inputs[tempX].NameFeedback.StringValue != null) +// { +// return Chassis.Inputs[tempX].NameFeedback.StringValue; +// } +// else +// { +// return ""; +// } +// }); +// OutputNameFeedbacks[tempX] = new StringFeedback(() => { +// if (Chassis.Outputs[tempX].NameFeedback.StringValue != null) +// { +// return Chassis.Outputs[tempX].NameFeedback.StringValue; +// } +// else +// { +// return ""; +// } +// }); +// OutputVideoRouteNameFeedbacks[tempX] = new StringFeedback(() => +// { +// if (Chassis.Outputs[tempX].VideoOutFeedback != null) +// { +// return Chassis.Outputs[tempX].VideoOutFeedback.NameFeedback.StringValue; +// } +// else +// { +// return ""; +// } +// }); +// OutputAudioRouteNameFeedbacks[tempX] = new StringFeedback(() => +// { +// if (Chassis.Outputs[tempX].AudioOutFeedback != null) +// { +// return Chassis.Outputs[tempX].AudioOutFeedback.NameFeedback.StringValue; +// } +// else +// { +// return ""; + +// } +// }); +// InputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => { return Chassis.Inputs[tempX].EndpointOnlineFeedback; }); + +// OutputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => { return Chassis.Outputs[tempX].EndpointOnlineFeedback; }); +// } +// } + +// /// +// /// +// /// +// /// +// /// +// public void AddInputCard(string type, uint number) +// { +// Debug.Console(2, this, "Adding input card '{0}', slot {1}", type, number); + +// if (type == "dmcHd") +// { +// var inputCard = new DmcHd(number, this.Chassis); +// var cecPort = inputCard.HdmiInput as ICec; +// AddHdmiInCardPorts(number, cecPort); +// } +// else if (type == "dmcHdDsp") +// { +// var inputCard = new DmcHdDsp(number, this.Chassis); +// var cecPort = inputCard.HdmiInput as ICec; +// AddHdmiInCardPorts(number, cecPort); +// } +// else if (type == "dmc4kHd") +// { +// var inputCard = new Dmc4kHd(number, this.Chassis); +// var cecPort = inputCard.HdmiInput as ICec; +// AddHdmiInCardPorts(number, cecPort); +// } +// else if (type == "dmc4kHdDsp") +// { +// var inputCard = new Dmc4kHdDsp(number, this.Chassis); +// var cecPort = inputCard.HdmiInput as ICec; +// AddHdmiInCardPorts(number, cecPort); +// } +// else if (type == "dmc4kzHd") +// { +// var inputCard = new Dmc4kzHd(number, this.Chassis); +// var cecPort = inputCard.HdmiInput as ICec; +// AddHdmiInCardPorts(number, cecPort); +// } +// else if (type == "dmc4kzHdDsp") +// { +// var inputCard = new Dmc4kzHdDsp(number, this.Chassis); +// var cecPort = inputCard.HdmiInput as ICec; +// AddHdmiInCardPorts(number, cecPort); +// } +// else if (type == "dmcC") +// { +// new DmcC(number, this.Chassis); +// AddDmInCardPorts(number); +// } +// else if (type == "dmcCDsp") +// { +// new DmcCDsp(number, this.Chassis); +// AddDmInCardPorts(number); +// } +// else if (type == "dmc4kC") +// { +// new Dmc4kC(number, this.Chassis); +// AddDmInCardPorts(number); +// } +// else if (type == "dmc4kCDsp") +// { +// new Dmc4kCDsp(number, this.Chassis); +// AddDmInCardPorts(number); +// } +// else if (type == "dmc4kzC") +// { +// new Dmc4kzC(number, this.Chassis); +// AddDmInCardPorts(number); +// } +// else if (type == "dmc4kzCDsp") +// { +// new Dmc4kzCDsp(number, this.Chassis); +// AddDmInCardPorts(number); +// } +// else if (type == "dmcCat") +// { +// new DmcCat(number, this.Chassis); +// AddDmInCardPorts(number); +// } +// else if (type == "dmcCatDsp") +// { +// new DmcCatDsp(number, this.Chassis); +// AddDmInCardPorts(number); +// } +// else if (type == "dmcS") +// { +// new DmcS(number, Chassis); +// AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmMmFiber); +// AddInCardHdmiAndAudioLoopPorts(number); +// } +// else if (type == "dmcSDsp") +// { +// new DmcSDsp(number, Chassis); +// AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmMmFiber); +// AddInCardHdmiAndAudioLoopPorts(number); +// } +// else if (type == "dmcS2") +// { +// new DmcS2(number, Chassis); +// AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmSmFiber); +// AddInCardHdmiAndAudioLoopPorts(number); +// } +// else if (type == "dmcS2Dsp") +// { +// new DmcS2Dsp(number, Chassis); +// AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmSmFiber); +// AddInCardHdmiAndAudioLoopPorts(number); +// } +// else if (type == "dmcSdi") +// { +// new DmcSdi(number, Chassis); +// AddInputPortWithDebug(number, "sdiIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Sdi); +// AddOutputPortWithDebug(number, "sdiOut", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Sdi, null); +// AddInCardHdmiAndAudioLoopPorts(number); +// } +// else if (type == "dmcDvi") +// { +// new DmcDvi(number, Chassis); +// AddInputPortWithDebug(number, "dviIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Dvi); +// AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); +// AddInCardHdmiLoopPort(number); +// } +// else if (type == "dmcVga") +// { +// new DmcVga(number, Chassis); +// AddInputPortWithDebug(number, "vgaIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Vga); +// AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); +// AddInCardHdmiLoopPort(number); +// } +// else if (type == "dmcVidBnc") +// { +// new DmcVidBnc(number, Chassis); +// AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); +// AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); +// AddInCardHdmiLoopPort(number); +// } +// else if (type == "dmcVidRcaA") +// { +// new DmcVidRcaA(number, Chassis); +// AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); +// AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); +// AddInCardHdmiLoopPort(number); +// } +// else if (type == "dmcVidRcaD") +// { +// new DmcVidRcaD(number, Chassis); +// AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); +// AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.DigitalAudio); +// AddInCardHdmiLoopPort(number); +// } +// else if (type == "dmcVid4") +// { +// new DmcVid4(number, Chassis); +// AddInputPortWithDebug(number, "compositeIn1", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); +// AddInputPortWithDebug(number, "compositeIn2", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); +// AddInputPortWithDebug(number, "compositeIn3", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); +// AddInputPortWithDebug(number, "compositeIn4", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); +// AddInCardHdmiLoopPort(number); +// } +// else if (type == "dmcStr") +// { +// new DmcStr(number, Chassis); +// AddInputPortWithDebug(number, "streamIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Streaming); +// AddInCardHdmiAndAudioLoopPorts(number); +// } +// } + +// void AddDmInCardPorts(uint number) +// { +// AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmCat); +// AddInCardHdmiAndAudioLoopPorts(number); +// } + +// void AddHdmiInCardPorts(uint number, ICec cecPort) +// { +// AddInputPortWithDebug(number, "hdmiIn", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, cecPort); +// AddInCardHdmiAndAudioLoopPorts(number); +// } + +// void AddInCardHdmiAndAudioLoopPorts(uint number) +// { +// AddOutputPortWithDebug(number, "hdmiLoopOut", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null); +// AddOutputPortWithDebug(number, "audioLoopOut", eRoutingSignalType.Audio, eRoutingPortConnectionType.Hdmi, null); +// } + +// void AddInCardHdmiLoopPort(uint number) +// { +// AddOutputPortWithDebug(number, "hdmiLoopOut", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null); +// } + +// /// +// /// +// /// +// /// +// /// +// public void AddOutputCard(string type, uint number) +// { +// Debug.Console(2, this, "Adding output card '{0}', slot {1}", type, number); +// if (type == "dmc4kHdo") +// { +// var outputCard = new Dmc4kHdoSingle(number, Chassis); +// var cecPort1 = outputCard.Card1.HdmiOutput; +// var cecPort2 = outputCard.Card2.HdmiOutput; +// AddDmcHdoPorts(number, cecPort1, cecPort2); +// } +// else if (type == "dmcHdo") +// { +// var outputCard = new DmcHdoSingle(number, Chassis); +// var cecPort1 = outputCard.Card1.HdmiOutput; +// var cecPort2 = outputCard.Card2.HdmiOutput; +// AddDmcHdoPorts(number, cecPort1, cecPort2); +// } +// else if (type == "dmc4kCoHd") +// { +// var outputCard = new Dmc4kCoHdSingle(number, Chassis); +// var cecPort1 = outputCard.Card1.HdmiOutput; +// AddDmcCoPorts(number, cecPort1); +// } +// else if (type == "dmc4kzCoHd") +// { +// var outputCard = new Dmc4kzCoHdSingle(number, Chassis); +// var cecPort1 = outputCard.Card1.HdmiOutput; +// AddDmcCoPorts(number, cecPort1); +// } +// else if (type == "dmcCoHd") +// { +// var outputCard = new DmcCoHdSingle(number, Chassis); +// var cecPort1 = outputCard.Card1.HdmiOutput; +// AddDmcCoPorts(number, cecPort1); +// } +// else if (type == "dmCatoHd") +// { +// var outputCard = new DmcCatoHdSingle(number, Chassis); +// var cecPort1 = outputCard.Card1.HdmiOutput; +// AddDmcCoPorts(number, cecPort1); +// } +// else if (type == "dmcSoHd") +// { +// var outputCard = new DmcSoHdSingle(number, Chassis); +// var cecPort1 = outputCard.Card1.HdmiOutput; +// AddOutputPortWithDebug(number, "dmOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmMmFiber, 2 * (number - 1) + 1); +// AddOutputPortWithDebug(number, "hdmiOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2 * (number - 1) + 1, cecPort1); +// AddOutputPortWithDebug(number, "dmOut2", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmMmFiber, 2 * (number - 1) + 2); + +// } +// else if (type == "dmcS2oHd") +// { +// var outputCard = new DmcS2oHdSingle(number, Chassis); +// var cecPort1 = outputCard.Card1.HdmiOutput; +// AddOutputPortWithDebug(number, "dmOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmSmFiber, 2 * (number - 1) + 1); +// AddOutputPortWithDebug(number, "hdmiOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2 * (number - 1) + 1, cecPort1); +// AddOutputPortWithDebug(number, "dmOut2", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmSmFiber, 2 * (number - 1) + 2); +// } +// else if (type == "dmcStro") +// { +// var outputCard = new DmcStroSingle(number, Chassis); +// AddOutputPortWithDebug(number, "streamOut", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Streaming, 2 * (number - 1) + 1); +// } + +// else +// Debug.Console(1, this, " WARNING: Output card type '{0}' is not available", type); +// } + +// void AddDmcHdoPorts(uint number, ICec cecPort1, ICec cecPort2) +// { +// AddOutputPortWithDebug(number, "hdmiOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2 * (number - 1) + 1, cecPort1); +// AddOutputPortWithDebug(number, "audioOut1", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio, 2 * (number - 1) + 1); +// AddOutputPortWithDebug(number, "hdmiOut2", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2 * (number - 1) + 2, cecPort2); +// AddOutputPortWithDebug(number, "audioOut2", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio, 2 * (number - 1) + 2); +// } + +// void AddDmcCoPorts(uint number, ICec cecPort1) +// { +// AddOutputPortWithDebug(number, "dmOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmCat, 2 * (number - 1) + 1); +// AddOutputPortWithDebug(number, "hdmiOut1", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2 * (number - 1) + 1, cecPort1); +// AddOutputPortWithDebug(number, "dmOut2", eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.DmCat, 2 * (number - 1) + 2); +// } + + +// /// +// /// Adds InputPort +// /// +// void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType) +// { +// var portKey = string.Format("inputCard{0}--{1}", cardNum, portName); +// Debug.Console(2, this, "Adding input port '{0}'", portKey); +// var inputPort = new RoutingInputPort(portKey, sigType, portType, cardNum, this); + +// InputPorts.Add(inputPort); +// } + +// /// +// /// Adds InputPort and sets Port as ICec object +// /// +// void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, ICec cecPort) +// { +// var portKey = string.Format("inputCard{0}--{1}", cardNum, portName); +// Debug.Console(2, this, "Adding input port '{0}'", portKey); +// var inputPort = new RoutingInputPort(portKey, sigType, portType, cardNum, this); + +// if (cecPort != null) +// inputPort.Port = cecPort; + +// InputPorts.Add(inputPort); +// } + +// /// +// /// Adds OutputPort +// /// +// void AddOutputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector) +// { +// var portKey = string.Format("outputCard{0}--{1}", cardNum, portName); +// Debug.Console(2, this, "Adding output port '{0}'", portKey); +// OutputPorts.Add(new RoutingOutputPort(portKey, sigType, portType, selector, this)); +// } + +// /// +// /// Adds OutputPort and sets Port as ICec object +// /// +// void AddOutputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector, ICec cecPort) +// { +// var portKey = string.Format("outputCard{0}--{1}", cardNum, portName); +// Debug.Console(2, this, "Adding output port '{0}'", portKey); +// var outputPort = new RoutingOutputPort(portKey, sigType, portType, selector, this); + +// if (cecPort != null) +// outputPort.Port = cecPort; + +// OutputPorts.Add(outputPort); +// } + +// /// +// /// +// /// +// void AddVolumeControl(uint number, Audio.Output audio) +// { +// VolumeControls.Add(number, new DmCardAudioOutputController(audio)); +// } + +// //public void SetInputHdcpSupport(uint input, ePdtHdcpSupport hdcpSetting) +// //{ + +// //} + + +// void Chassis_DMSystemChange(Switch device, DMSystemEventArgs args) { + +// } +// void Chassis_DMInputChange(Switch device, DMInputEventArgs args) { +// //Debug.Console(2, this, "DMSwitch:{0} Input:{1} Event:{2}'", this.Name, args.Number, args.EventId.ToString()); + +// switch (args.EventId) { +// case (DMInputEventIds.OnlineFeedbackEventId): { +// Debug.Console(2, this, "DMINput OnlineFeedbackEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback); +// InputEndpointOnlineFeedbacks[args.Number].FireUpdate(); +// break; +// } +// case (DMInputEventIds.VideoDetectedEventId): { +// Debug.Console(2, this, "DM Input {0} VideoDetectedEventId", args.Number); +// VideoInputSyncFeedbacks[args.Number].FireUpdate(); +// break; +// } +// case (DMInputEventIds.InputNameEventId): { +// Debug.Console(2, this, "DM Input {0} NameFeedbackEventId", args.Number); +// InputNameFeedbacks[args.Number].FireUpdate(); +// break; +// } +// } +// } +// /// +// /// +// void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) +// { + +// //This should be a switch case JTA 2018-07-02 +// var output = args.Number; +// if (args.EventId == DMOutputEventIds.VolumeEventId && +// VolumeControls.ContainsKey(output)) +// { +// VolumeControls[args.Number].VolumeEventFromChassis(); +// } +// else if (args.EventId == DMOutputEventIds.OnlineFeedbackEventId) +// { +// OutputEndpointOnlineFeedbacks[output].FireUpdate(); +// } +// else if (args.EventId == DMOutputEventIds.VideoOutEventId) +// { +// if (Chassis.Outputs[output].VideoOutFeedback != null) +// { +// Debug.Console(2, this, "DMSwitchVideo:{0} Routed Input:{1} Output:{2}'", this.Name, Chassis.Outputs[output].VideoOutFeedback.Number, output); +// } +// if (VideoOutputFeedbacks.ContainsKey(output)) +// { +// VideoOutputFeedbacks[output].FireUpdate(); + +// } +// if (OutputVideoRouteNameFeedbacks.ContainsKey(output)) +// { +// OutputVideoRouteNameFeedbacks[output].FireUpdate(); +// } +// } +// else if (args.EventId == DMOutputEventIds.AudioOutEventId) +// { +// if (Chassis.Outputs[output].AudioOutFeedback != null) +// { +// Debug.Console(2, this, "DMSwitchAudio:{0} Routed Input:{1} Output:{2}'", this.Name, Chassis.Outputs[output].AudioOutFeedback.Number, output); +// } +// if (AudioOutputFeedbacks.ContainsKey(output)) +// { +// AudioOutputFeedbacks[output].FireUpdate(); +// } +// } +// else if (args.EventId == DMOutputEventIds.OutputNameEventId) +// { +// Debug.Console(2, this, "DM Output {0} NameFeedbackEventId", output); +// OutputNameFeedbacks[output].FireUpdate(); +// } + +// } + +// /// +// /// +// /// +// /// +// void StartOffTimer(PortNumberType pnt) +// { +// if (RouteOffTimers.ContainsKey(pnt)) +// return; +// RouteOffTimers[pnt] = new CTimer(o => +// { +// ExecuteSwitch(0, pnt.Number, pnt.Type); +// }, RouteOffTime); +// } + + +// // Send out sigs when coming online +// void IsOnline_OutputChange(object sender, EventArgs e) +// { +// if (IsOnline.BoolValue) +// { +// if (InputNames != null) +// foreach (var kvp in InputNames) +// Chassis.Inputs[kvp.Key].Name.StringValue = kvp.Value; +// if (OutputNames != null) +// foreach(var kvp in OutputNames) +// Chassis.Outputs[kvp.Key].Name.StringValue = kvp.Value; +// } +// } + +// #region IRouting Members + +// public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType sigType) +// { +// Debug.Console(2, this, "Making an awesome DM route from {0} to {1} {2}", inputSelector, outputSelector, sigType); + +// var input = Convert.ToUInt32(inputSelector); // Cast can sometimes fail +// var output = Convert.ToUInt32(outputSelector); +// // Check to see if there's an off timer waiting on this and if so, cancel +// var key = new PortNumberType(output, sigType); +// if (input == 0) +// { +// StartOffTimer(key); +// } +// else +// { +// if(RouteOffTimers.ContainsKey(key)) +// { +// Debug.Console(2, this, "{0} cancelling route off due to new source", output); +// RouteOffTimers[key].Stop(); +// RouteOffTimers.Remove(key); +// } +// } + +// Card.DMICard inCard = input == 0 ? null : Chassis.Inputs[input]; + +// // NOTE THAT THESE ARE NOTS - TO CATCH THE AudioVideo TYPE +// if (sigType != eRoutingSignalType.Audio) +// { +// Chassis.VideoEnter.BoolValue = true; +// Chassis.Outputs[output].VideoOut = inCard; +// } + +// if (sigType != eRoutingSignalType.Video) +// { +// Chassis.AudioEnter.BoolValue = true; +// Chassis.Outputs[output].AudioOut = inCard; +// } +// } + +// #endregion + +// } + +// //public struct PortNumberType +// //{ +// // public uint Number { get; private set; } +// // public eRoutingSignalType Type { get; private set; } + +// // public PortNumberType(uint number, eRoutingSignalType type) : this() +// // { +// // Number = number; +// // Type = type; +// // } +// //} +//} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Properties/AssemblyInfo.cs b/essentials-framework/Essentials DM/Essentials_DM/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..d11fa984 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyTitle("Essentials_DM")] +[assembly: AssemblyCompany("PepperDash Technology Corp")] +[assembly: AssemblyProduct("Essentials_DM")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyVersion("1.3.*")] \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Properties/ControlSystem.cfg b/essentials-framework/Essentials DM/Essentials_DM/Properties/ControlSystem.cfg new file mode 100644 index 00000000..e69de29b diff --git a/essentials-framework/Essentials DM/Essentials_DM/VideoStatusHelpers.cs b/essentials-framework/Essentials DM/Essentials_DM/VideoStatusHelpers.cs new file mode 100644 index 00000000..096b1e36 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/VideoStatusHelpers.cs @@ -0,0 +1,73 @@ +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 PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.DM +{ + /// + /// These methods will get the funcs that return values from various video port types... + /// + public class VideoStatusHelper + { + public static VideoStatusFuncsWrapper GetHdmiInputStatusFuncs(HdmiInputPort port) + { + return new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => port.VideoAttributes.HdcpActiveFeedback.BoolValue, + HdcpStateFeedbackFunc = () => port.VideoAttributes.HdcpStateFeedback.ToString(), + VideoResolutionFeedbackFunc = () => port.VideoAttributes.GetVideoResolutionString(), + VideoSyncFeedbackFunc = () => port.SyncDetectedFeedback.BoolValue + }; + } + + public static VideoStatusFuncsWrapper GetHdmiInputStatusFuncs(EndpointHdmiInput port) + { + return new VideoStatusFuncsWrapper + { + VideoResolutionFeedbackFunc = () => port.VideoAttributes.GetVideoResolutionString(), + VideoSyncFeedbackFunc = () => port.SyncDetectedFeedback.BoolValue + }; + } + + public static VideoStatusFuncsWrapper GetVgaInputStatusFuncs(EndpointVgaInput port) + { + return new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => port.VideoAttributes.HdcpActiveFeedback.BoolValue, + HdcpStateFeedbackFunc = () => port.VideoAttributes.HdcpStateFeedback.ToString(), + VideoResolutionFeedbackFunc = () => port.VideoAttributes.GetVideoResolutionString(), + VideoSyncFeedbackFunc = () => port.SyncDetectedFeedback.BoolValue + }; + } + + public static VideoStatusFuncsWrapper GetDmInputStatusFuncs(DMInputPort port) + { + return new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => port.VideoAttributes.HdcpActiveFeedback.BoolValue, + HdcpStateFeedbackFunc = () => port.VideoAttributes.HdcpStateFeedback.ToString(), + VideoResolutionFeedbackFunc = () => port.VideoAttributes.GetVideoResolutionString(), + VideoSyncFeedbackFunc = () => port.SyncDetectedFeedback.BoolValue + }; + } + + public static VideoStatusFuncsWrapper GetDisplayPortInputStatusFuncs(EndpointDisplayPortInput port) + { + return new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => port.VideoAttributes.HdcpActiveFeedback.BoolValue, + HdcpStateFeedbackFunc = () => port.VideoAttributes.HdcpStateFeedback.ToString(), + VideoResolutionFeedbackFunc = () => port.VideoAttributes.GetVideoResolutionString(), + VideoSyncFeedbackFunc = () => port.SyncDetectedFeedback.BoolValue + }; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/app.config b/essentials-framework/Essentials DM/Essentials_DM/app.config new file mode 100644 index 00000000..58e92509 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common.sln b/essentials-framework/Essentials Devices Common/Essentials Devices Common.sln new file mode 100644 index 00000000..84fa1346 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Essentials Devices Common", "Essentials Devices Common\Essentials Devices Common.csproj", "{892B761C-E479-44CE-BD74-243E9214AF13}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {892B761C-E479-44CE-BD74-243E9214AF13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {892B761C-E479-44CE-BD74-243E9214AF13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {892B761C-E479-44CE-BD74-243E9214AF13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {892B761C-E479-44CE-BD74-243E9214AF13}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Audio/GenericAudioOut.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Audio/GenericAudioOut.cs new file mode 100644 index 00000000..d9ba06cf --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Audio/GenericAudioOut.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Common +{ + /// + /// Represents and audio endpoint + /// + public class GenericAudioOut : Device, IRoutingSinkNoSwitching + { + public RoutingInputPort AnyAudioIn { get; private set; } + + public GenericAudioOut(string key, string name) + : base(key, name) + { + AnyAudioIn = new RoutingInputPort(RoutingPortNames.AnyAudioIn, eRoutingSignalType.Audio, + eRoutingPortConnectionType.LineAudio, null, this); + } + + #region IRoutingInputs Members + + public RoutingPortCollection InputPorts + { + get { return new RoutingPortCollection { AnyAudioIn }; } + } + + #endregion + } + + + /// + /// Allows a zone-device's audio control to be attached to the endpoint, for easy routing and + /// control switching. Will also set the zone name on attached devices, like SWAMP or other + /// hardware with names built in. + /// + public class GenericAudioOutWithVolume : GenericAudioOut, IHasVolumeDevice + { + public string VolumeDeviceKey { get; private set; } + public uint VolumeZone { get; private set; } + + public IBasicVolumeControls VolumeDevice + { + get + { + var dev = DeviceManager.GetDeviceForKey(VolumeDeviceKey); + if (dev is IAudioZones) + return (dev as IAudioZones).Zone[VolumeZone]; + else return dev as IBasicVolumeControls; + } + } + + /// + /// Constructor - adds the name to the attached audio device, if appropriate. + /// + /// + /// + /// + /// + public GenericAudioOutWithVolume(string key, string name, string audioDevice, uint zone) + : base(key, name) + { + VolumeDeviceKey = audioDevice; + VolumeZone = zone; + } + + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/AudioCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/AudioCodecBase.cs new file mode 100644 index 00000000..15a998a4 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/AudioCodecBase.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; + +namespace PepperDash.Essentials.Devices.Common.AudioCodec +{ + public abstract class AudioCodecBase : Device, IHasDialer, IUsageTracking, IAudioCodecInfo + { + + public event EventHandler CallStatusChange; + + public AudioCodecInfo CodecInfo { get; protected set; } + + #region IUsageTracking Members + + /// + /// This object can be added by outside users of this class to provide usage tracking + /// for various services + /// + public UsageTracking UsageTracker { get; set; } + + #endregion + + /// + /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected + /// + public bool IsInCall + { + get + { + bool value; + + if (ActiveCalls != null) + value = ActiveCalls.Any(c => c.IsActiveCall); + else + value = false; + return value; + } + } + + // In most cases only a single call can be active + public List ActiveCalls { get; set; } + + public AudioCodecBase(string key, string name) + : base(key, name) + { + ActiveCalls = new List(); + } + + /// + /// Helper method to fire CallStatusChange event with old and new status + /// + protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call) + { + call.Status = newStatus; + + OnCallStatusChange(call); + + } + + /// + /// + /// + /// + /// + /// + protected void OnCallStatusChange(CodecActiveCallItem item) + { + var handler = CallStatusChange; + if (handler != null) + handler(this, new CodecCallStatusItemChangeEventArgs(item)); + + if (UsageTracker != null) + { + if (IsInCall && !UsageTracker.UsageTrackingStarted) + UsageTracker.StartDeviceUsage(); + else if (UsageTracker.UsageTrackingStarted && !IsInCall) + UsageTracker.EndDeviceUsage(); + } + } + + #region IHasDialer Members + + public abstract void Dial(string number); + + public abstract void EndCall(CodecActiveCallItem activeCall); + + public abstract void EndAllCalls(); + + public abstract void AcceptCall(CodecActiveCallItem item); + + public abstract void RejectCall(CodecActiveCallItem item); + + public abstract void SendDtmf(string digit); + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/Interfaces/IAudioCodecInfo.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/Interfaces/IAudioCodecInfo.cs new file mode 100644 index 00000000..e853c898 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/Interfaces/IAudioCodecInfo.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.AudioCodec +{ + /// + /// Implements a common set of data about a codec + /// + public interface IAudioCodecInfo + { + AudioCodecInfo CodecInfo { get; } + } + + /// + /// Stores general information about a codec + /// + public abstract class AudioCodecInfo + { + public abstract string PhoneNumber { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/Interfaces/IHasAudioCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/Interfaces/IHasAudioCodec.cs new file mode 100644 index 00000000..e4cb5ee4 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/Interfaces/IHasAudioCodec.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.AudioCodec +{ + /// + /// For rooms that have audio codec + /// + public interface IHasAudioCodec + { + AudioCodecBase AudioCodec { get; } + BoolFeedback InCallFeedback { get; } + + ///// + ///// Make this more specific + ///// + //List ActiveCalls { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/MockAC/MockAC.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/MockAC/MockAC.cs new file mode 100644 index 00000000..e0645624 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/MockAC/MockAC.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; + +namespace PepperDash.Essentials.Devices.Common.AudioCodec +{ + public class MockAC : AudioCodecBase + { + public MockAC(string key, string name, MockAcPropertiesConfig props) + : base(key, name) + { + CodecInfo = new MockAudioCodecInfo(); + + CodecInfo.PhoneNumber = props.PhoneNumber; + } + + public override void Dial(string number) + { + if (!IsInCall) + { + Debug.Console(1, this, "Dial: {0}", number); + var call = new CodecActiveCallItem() + { + Name = "Mock Outgoing Call", + Number = number, + Type = eCodecCallType.Audio, + Status = eCodecCallStatus.Connected, + Direction = eCodecCallDirection.Outgoing, + Id = "mockAudioCall-1" + }; + + ActiveCalls.Add(call); + + OnCallStatusChange(call); + } + else + { + Debug.Console(1, this, "Already in call. Cannot dial new call."); + } + } + + public override void EndCall(CodecActiveCallItem call) + { + Debug.Console(1, this, "EndCall"); + ActiveCalls.Remove(call); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call); + } + + public override void EndAllCalls() + { + Debug.Console(1, this, "EndAllCalls"); + for (int i = ActiveCalls.Count - 1; i >= 0; i--) + { + var call = ActiveCalls[i]; + ActiveCalls.Remove(call); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call); + } + } + + public override void AcceptCall(CodecActiveCallItem call) + { + Debug.Console(1, this, "AcceptCall"); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connecting, call); + } + + public override void RejectCall(CodecActiveCallItem call) + { + Debug.Console(1, this, "RejectCall"); + ActiveCalls.Remove(call); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call); + } + + public override void SendDtmf(string s) + { + Debug.Console(1, this, "BEEP BOOP SendDTMF: {0}", s); + } + + /// + /// + /// + /// + public void TestIncomingAudioCall(string number) + { + Debug.Console(1, this, "TestIncomingAudioCall from {0}", number); + var call = new CodecActiveCallItem() { Name = number, Id = number, Number = number, Type = eCodecCallType.Audio, Direction = eCodecCallDirection.Incoming }; + ActiveCalls.Add(call); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Ringing, call); + } + + } + + public class MockAudioCodecInfo : AudioCodecInfo + { + string _phoneNumber; + + public override string PhoneNumber + { + get + { + return _phoneNumber; + } + set + { + _phoneNumber = value; + } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/MockAC/MockAcPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/MockAC/MockAcPropertiesConfig.cs new file mode 100644 index 00000000..7ae375b7 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/AudioCodec/MockAC/MockAcPropertiesConfig.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Devices.Common.AudioCodec +{ + public class MockAcPropertiesConfig + { + [JsonProperty("phoneNumber")] + public string PhoneNumber { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs new file mode 100644 index 00000000..8a7dcf03 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraBase.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; +using System.Text.RegularExpressions; +using Crestron.SimplSharp.Reflection; + + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + public enum eCameraCapabilities + { + None = 0, + Pan = 1, + Tilt = 2, + Zoom = 4, + Focus = 8 + } + + public abstract class CameraBase : Device + { + public bool CanPan + { + get + { + return (Capabilities & eCameraCapabilities.Pan) == eCameraCapabilities.Pan; + } + } + + public bool CanTilt + { + get + { + return (Capabilities & eCameraCapabilities.Tilt) == eCameraCapabilities.Tilt; + } + } + + public bool CanZoom + { + get + { + return (Capabilities & eCameraCapabilities.Zoom) == eCameraCapabilities.Zoom; + } + } + + public bool CanFocus + { + get + { + return (Capabilities & eCameraCapabilities.Focus) == eCameraCapabilities.Focus; + } + } + + // A bitmasked value to indicate the movement capabilites of this camera + protected eCameraCapabilities Capabilities { get; set; } + + public CameraBase(string key, string name) : + base(key, name) { } + } + + public class CameraPropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public ControlPropertiesConfig Control { get; set; } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs new file mode 100644 index 00000000..182b560c --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; +using System.Text.RegularExpressions; +using Crestron.SimplSharp.Reflection; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + public class CameraVisca : CameraBase, IHasCameraPtzControl, ICommunicationMonitor + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + public byte PanSpeed = 0x10; + public byte TiltSpeed = 0x10; + private bool IsMoving; + private bool IsZooming; + public bool PowerIsOn { get; private set; } + + byte[] IncomingBuffer = new byte[] { }; + public BoolFeedback PowerIsOnFeedback { get; private set; } + + public CameraVisca(string key, string name, IBasicCommunication comm, CameraPropertiesConfig props) : + base(key, name) + { + + // Default to all capabilties + Capabilities = eCameraCapabilities.Pan | eCameraCapabilities.Tilt | eCameraCapabilities.Zoom | eCameraCapabilities.Focus; + + Communication = comm; + var socket = comm as ISocketStatus; + if (socket != null) + { + // This instance uses IP control + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + else + { + // This instance uses RS-232 control + } + PortGather = new CommunicationGather(Communication, "\xFF"); + + + Communication.BytesReceived += new EventHandler(Communication_BytesReceived); + PowerIsOnFeedback = new BoolFeedback(() => { return PowerIsOn; }); + + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 20000, 120000, 300000, "\x81\x09\x04\x00\xFF"); + } + DeviceManager.AddDevice(CommunicationMonitor); + + + } + public override bool CustomActivate() + { + Communication.Connect(); + + + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + + + CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + return true; + } + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString()); + + if (e.Client.IsConnected) + { + + } + else + { + + } + } + + + void SendBytes(byte[] b) + { + + 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, "Sending:{0}", ComTextHelper.GetEscapedText(b)); + + Communication.SendBytes(b); + } + 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)); + } + + + private void SendPanTiltCommand (byte[] cmd) + { + var temp = new Byte[] { 0x81, 0x01, 0x06, 0x01, PanSpeed, TiltSpeed }; + int length = temp.Length + cmd.Length + 1; + + byte[] sum = new byte[length]; + temp.CopyTo(sum, 0); + cmd.CopyTo(sum, temp.Length); + sum[length - 1] = 0xFF; + SendBytes(sum); + } + + public void PowerOn() + { + + SendBytes(new Byte[] { 0x81, 0x01, 0x04, 0x00, 0x02, 0xFF }); + } + + public void PowerOff() + { + SendBytes(new Byte[] {0x81, 0x01, 0x04, 0x00, 0x03, 0xFF}); + } + public void PanLeft() + { + SendPanTiltCommand(new byte[] {0x01, 0x03}); + IsMoving = true; + } + public void PanRight() + { + SendPanTiltCommand(new byte[] { 0x02, 0x03 }); + IsMoving = true; + } + public void PanStop() + { + Stop(); + } + public void TiltDown() + { + SendPanTiltCommand(new byte[] { 0x03, 0x02 }); + IsMoving = true; + } + public void TiltUp() + { + SendPanTiltCommand(new byte[] { 0x03, 0x01 }); + IsMoving = true; + } + public void TiltStop() + { + Stop(); + } + + private void SendZoomCommand (byte cmd) + { + SendBytes(new byte[] {0x81, 0x01, 0x04, 0x07, cmd, 0xFF} ); + } + public void ZoomIn() + { + SendZoomCommand(0x02); + IsZooming = true; + } + public void ZoomOut() + { + SendZoomCommand(0x03); + IsZooming = true; + } + public void ZoomStop() + { + Stop(); + } + + public void Stop() + { + if (IsZooming) + { + SendZoomCommand(0x00); + IsZooming = false; + } + else + { + SendPanTiltCommand(new byte[] {0x03, 0x03}); + IsMoving = false; + } + } + public void PositionHome() + { + throw new NotImplementedException(); + } + public void RecallPreset(int presetNumber) + { + SendBytes(new byte[] {0x81, 0x01, 0x04, 0x3F, 0x02, (byte)presetNumber, 0xFF} ); + } + public void SavePreset(int presetNumber) + { + SendBytes(new byte[] { 0x81, 0x01, 0x04, 0x3F, 0x01, (byte)presetNumber, 0xFF }); + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/ClassDiagram1.cd b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ClassDiagram1.cd new file mode 100644 index 00000000..8b9d1dd0 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ClassDiagram1.cd @@ -0,0 +1,740 @@ + + + + + + AAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA= + Audio\GenericAudioOut.cs + + + + + + + AAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAgAAAAQA= + Audio\GenericAudioOut.cs + + + + + + + AAAAAAAAAAAAAAAAAAAIIAAAAAAACABAAAAAAAAAAAA= + Crestron\Gateways\CenRfgwController.cs + + + + + + gUyjAAoIAAAAxgYAABAAgAECABAoAEAQqcCAQHIABAI= + DiscPlayer\IRDiscPlayerBase.cs + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAA= + Factory\DeviceFactory.cs + + + + + + AAgAAAAAAAACAAAAAAAAAAAAAAAgAAAAAAAAAAIAAAA= + Generic\GenericSource.cs + + + + + + + AAwAAAAAAAACAAAAAAACAAAAAQAgAAAAAAAAAAIAAAA= + PC\InRoomPc.cs + + + + + + + AAwAAAAAAAACAAAAAAACAAAAAQAgAAAAAAAAAAIAAAA= + PC\Laptop.cs + + + + + + + AUgjCAqAEAASwgYAACAAoAECABAoAEAwrcCAQHKABAI= + SetTopBox\IRSetTopBoxBase.cs + + + + + + + AAAAAAAEEAAQAAAAAAAAIAAAAAAAAAAAAAAAAACAAAA= + SetTopBox\SetTopBoxPropertiesConfig.cs + + + + + + AAggAAIAAAAARAAAAAAAgAACABAoAEAQqACAQAAABAI= + Streaming\AppleTV.cs + + + + + + + AAggAAIAAAAABAAAAAAAgAACABAoAEAQqACAQAAABAI= + Streaming\Roku.cs + + + + + + + AAACAAAEAAAAAAAAAOAAAAQAAAAAAAAAAQAAAAAAAAA= + Codec\CodecActiveCallItem.cs + + + + + + AAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Codec\CodecActiveCallItem.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAA= + Codec\eCodecCallDirection.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAA= + Codec\eCodecCallStatus.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA= + Codec\eCodecCallType.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAA= + Codec\eMeetingPrivacy.cs + + + + + + gIAAAAAAAAAAAACCAACAAAAAAAAAAAACAAAAAABAAAA= + Codec\iCodecInfo.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA= + Codec\iHasCallFavorites.cs + + + + + + AAAAAAAQAAAAAgAAAAAAAAAAAAAAGAAAABAAAAAAACQ= + Codec\iHasCallHistory.cs + + + + + + AAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Codec\iHasDirectory.cs + + + + + + AAAAAAAAAAAIAAAAAAAAAAAABAAAAAIAAIAAQAAAAAA= + Codec\iHasDirectory.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA= + Codec\iHasDirectory.cs + + + + + + BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAA= + Codec\iHasDirectory.cs + + + + + + AAAAAgAAAAAAAEAAAAAAAAAAAAAAAAEIAAAAAAAAAAA= + Codec\iHasDirectory.cs + + + + + + AAAAAIAAAAAAAgAAAEAAAAAAAAAIAAAAAAAAAAAAAAA= + Codec\iHasDirectory.cs + + + + + + AAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Codec\iHasDirectory.cs + + + + + + AAAAAABAAAAAEAAAAAAAgAAEAAAABAAAAAAAIQAAAAA= + Codec\iHasScheduleAwareness.cs + + + + + + AAACAAAAAAACEEgAAAAAAAgQIAAoAQABAAQAAEgAAAA= + Codec\iHasScheduleAwareness.cs + + + + + + AAAAAAAAAAAAAAAAAEAAAABAAAAIAAAAAAAAAAgAAAA= + Codec\iHasScheduleAwareness.cs + + + + + + AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAQAAAAA= + Codec\iHasScheduleAwareness.cs + + + + + + AAAAAAAAAAAQAAAAAAAAAAAAAAAAAIQAAACAABAAAAA= + VideoCodec\CiscoCodec\CiscoSparkCodecPropertiesConfig.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA= + VideoCodec\CiscoCodec\CiscoSparkCodecPropertiesConfig.cs + + + + + + hC4gIEAIUAABoQAEgBACAAAdBC1gmsGRICKGdQNBACw= + Display\AvocorVTFDisplay.cs + + + + + + + AAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAEQAAAAA= + Display\ComTcpDisplayBase.cs + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAA= + Display\DeviceFactory.cs + + + + + + ogSIAAKIAAALCAAEABAQEAgABAAAAMIAAAAAEABAACA= + Display\NecPaSeriesProjector.cs + + + + + + hLQgIShIAABFQQRUAvICDRMQESwSEQKRKACCZQNAESY= + Display\NECPSXMDisplay.cs + + + + + + + hKwwIEAIQAABoUAGgBACAAAEBGxAmYORKCCGZQJAACw= + Display\SamsungMDCDisplay.cs + + + + + + + gACAAIAAAAEAQYCigAAAAAAQAACgCAAAAAAAAAMAAAI= + DSP\BiampTesira\BiampTesiraForteDsp.cs + + + + + + RAAgJACAAAAAAYAAAkIAAAAAIAQCEACJABACRAAAAUQ= + DSP\BiampTesira\BiampTesiraForteDspLevel.cs + + + + + + + BAAAAAAEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAA= + DSP\BiampTesira\BiampTesiraFortePropertiesConfig.cs + + + + + + AAAAAAAAEAAAAIAAAAAAAAAAAAAAAAAIAAEAAAAAAEQ= + DSP\BiampTesira\BiampTesiraFortePropertiesConfig.cs + + + + + + AAAAIAAAAACAAQQAAAAAQAAAAAAAIAAAABEAAAAAAEA= + DSP\BiampTesira\TesiraForteControlPoint.cs + + + + + + AAAAAAAAAAAEAIAgAAAAAAIAAAAAAAAAAAAAAAAAAAA= + DSP\DspBase.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + DSP\DspBase.cs + + + + + + BAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAQAAAAAQ= + DSP\DspBase.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAA= + DSP\PolycomSoundStructure\SoundStructureBasics.cs + + + + + + gACAEBAAAoAABQBAASAAAAAAAAEgAAACAAACAAMAQAI= + Environment\Lutron\LutronQuantum.cs + + + + + + + AAAAAAAEAAAAAAAAgQAAAAAAAAAAAAQCAAACAAAAAAA= + Environment\Lutron\LutronQuantum.cs + + + + + + gQAABAAAAxAAIACAgAgAgCBAUQAAQAgCIAEQAACBAAA= + Microphone\MicrophonePrivacyController.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAEAAAAAgAIAAAAAAAAAA= + Microphone\MicrophonePrivacyControllerConfig.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA= + Microphone\MicrophonePrivacyControllerConfig.cs + + + + + + AAAAAAAAAAIAAAAAAAABAABgAAAAAASEABAAAAAAAAA= + Occupancy\EssentialsGlsOccupancySensorBaseController.cs + + + + + + + AAACBAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Occupancy\EssentialsOccupancyAggregator.cs + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg= + VideoCodec\CiscoCodec\BookingsDataClasses.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + VideoCodec\CiscoCodec\CallHistoryDataClasses.cs + + + + + + EAAAABAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAA= + VideoCodec\CiscoCodec\PhonebookDataClasses.cs + + + + + + BgYAIAACAQaKBACAAwAAUSQAAWYCEACDAAiAQBBCgQU= + VideoCodec\MockVC\MockVC.cs + + + + + + + gKAAAAAAAAAAAACCAACAAAAAAAAAAAACAAAAAABBAAA= + VideoCodec\MockVC\MockVC.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA= + VideoCodec\MockVC\MockVcPropertiesConfig.cs + + + + + + BiwgAAECAASAAECAgQEDIABAAAYiEBCpCEigQBZCAQU= + VideoCodec\VideoCodecBase.cs + + + + + + + EAiCAAQAAgEAAQQAEAAAAAAAAAEAAAAAAACAAAAAAAM= + VideoCodec\VideoCodecBase.cs + + + + + + + jhQEtAJaASb7kSCwAwtxECSABsf2n1GBJEmAVJFKWTc= + VideoCodec\CiscoCodec\CiscoSparkCodec.cs + + + + + + + AAAAAAAAEAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA= + VideoCodec\CiscoCodec\CiscoSparkCodec.cs + + + + + + AIAAAAQACAEAAAQAEAAAAAAAAAAgAAAAAAAACAACACE= + VideoCodec\CiscoCodec\CiscoSparkCodec.cs + + + + + + + AAAAQAAAACAAAAABAAAAAAAIAIAAAAAAAQAAgAAAAAA= + VideoCodec\CiscoCodec\HttpApiServer.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + VideoCodec\CiscoCodec\xConfiguration.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + VideoCodec\CiscoCodec\xEvent.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + VideoCodec\CiscoCodec\xStatus.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + VideoCodec\CiscoCodec\xStatusSparkPlus.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Codec\iCodecAudio.cs + + + + + + AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAA= + Codec\iCodecInfo.cs + + + + + + AAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Codec\iHasCallFavorites.cs + + + + + + AAAAAAAAAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAA= + Codec\iHasCallHistory.cs + + + + + + AAAAAAACAAAAAAAAAAAAAAAAAAAAAAAIAEAgABAAAAA= + Codec\iHasContentSharing.cs + + + + + + AAgAAAACAAAAAACAAAAAAAAAAAICAAAAAAAAAAQCAAA= + Codec\iHasDialer.cs + + + + + + AAAAAAAAAAAAgAAAAAAAAAAAAgCAAAAABAAAEAAAAAA= + Codec\iHasDirectory.cs + + + + + + AAAAAAAAAAAAAAAAAAAAACQAAAAAAAAAAAAAAAAAAAA= + Codec\iHasScheduleAwareness.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA= + Display\InputInterfaces.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA= + Display\InputInterfaces.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAA= + Display\InputInterfaces.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAA= + Display\InputInterfaces.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA= + Display\InputInterfaces.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAA= + Display\InputInterfaces.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAA= + Display\InputInterfaces.cs + + + + + + QAAAAACAAAAAAAAAAAAAAAAAAAAAAAAIAAEAAAAAAAQ= + DSP\DspBase.cs + + + + + + AAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Occupancy\iOccupancyStatusProvider.cs + + + + + + AAAAAAAAAAAgAAAAAAAAAAAAAAAEEgAAAAAAAAAAAAA= + VideoCodec\Interfaces\IHasCodecLayouts.cs + + + + + + ABAAEAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAABAAAAI= + VideoCodec\Interfaces\IHasCodecSelfview.cs + + + + + + AAAAAAAgAAAAAAAAAAAAAIAAAEAAAAAAAAAAAAAAAAA= + Crestron\Gateways\CenRfgwController.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAAAAAABA= + Codec\eCodecCallDirection.cs + + + + + + CAgAAABAAAAAQAACAAABAAAAAAAAAAABCAAAAiAAQBA= + Codec\eCodecCallStatus.cs + + + + + + AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAoBg= + Codec\eCodecCallType.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAQBA= + Codec\eMeetingPrivacy.cs + + + + + + AAAAAAABAAAAAIAAAAAAAAAAAAAAAAAAAEAAAAAAABA= + Codec\iHasCallHistory.cs + + + + + + BAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAIAAAAAAABg= + Codec\iHasDirectory.cs + + + + + + AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAABg= + Codec\iHasDirectory.cs + + + + + + BAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAIACg= + Codec\iHasScheduleAwareness.cs + + + + + + AACAACAAABAAgIAAgAAQAAAAAAAAIAAAIAAAACAEAAg= + Environment\Lutron\LutronQuantum.cs + + + + + + AAAAAAAAAAAACAQAAAAABAAEAAAAAAAAgAAAAAAAAAA= + VideoCodec\CiscoCodec\CiscoSparkCodec.cs + + + + \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs new file mode 100644 index 00000000..75c4fc1a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace PepperDash.Essentials.Devices.Common.Codec + +{ + public class CodecActiveCallItem + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("number")] + public string Number { get; set; } + + [JsonProperty("type")] + [JsonConverter(typeof(StringEnumConverter))] + public eCodecCallType Type { get; set; } + + [JsonProperty("status")] + [JsonConverter(typeof(StringEnumConverter))] + public eCodecCallStatus Status { get; set; } + + [JsonProperty("direction")] + [JsonConverter(typeof(StringEnumConverter))] + public eCodecCallDirection Direction { get; set; } + + [JsonProperty("id")] + public string Id { get; set; } + + //public object CallMetaData { get; set; } + + /// + /// Returns true when this call is any status other than + /// Unknown, Disconnected, Disconnecting + /// + [JsonProperty("isActiveCall")] + public bool IsActiveCall + { + get + { + return !(Status == eCodecCallStatus.Disconnected + || Status == eCodecCallStatus.Disconnecting + || Status == eCodecCallStatus.Idle + || Status == eCodecCallStatus.Unknown); + } + } + } + + /// + /// + /// + public class CodecCallStatusItemChangeEventArgs : EventArgs + { + public CodecActiveCallItem CallItem { get; private set; } + + public CodecCallStatusItemChangeEventArgs(CodecActiveCallItem item) + { + CallItem = item; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs.orig b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs.orig new file mode 100644 index 00000000..46746037 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs.orig @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.Codec + +{ + public class CodecActiveCallItem + { + public string Name { get; set; } + + public string Number { get; set; } + +<<<<<<< HEAD:Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs + public eCodecCallType Type { get; set; } + + public eCodecCallStatus Status { get; set; } + + public string Id { get; set; } +======= + public eCodecCallType Type { get; set; } + + public eCodecCallStatus Status { get; set; } + + public string Id { get; set; } + + public object CallMetaData { get; set; } +>>>>>>> origin/feature/cisco-spark-2:Essentials Devices Common/Essentials Devices Common/VideoCodec/CodecActiveCallItem.cs + } + + public enum eCodecCallType + { + Unknown = 0, Audio, Video +<<<<<<< HEAD:Essentials Devices Common/Essentials Devices Common/Codec/CodecActiveCallItem.cs + } + + public enum eCodecCallStatus + { + Unknown = 0, Dialing, Connected, Incoming, OnHold, Disconnected +======= + } + + public enum eCodecCallStatus + { + Unknown = 0, Dialing, Connected, Incoming, OnHold, Disconnected +>>>>>>> origin/feature/cisco-spark-2:Essentials Devices Common/Essentials Devices Common/VideoCodec/CodecActiveCallItem.cs + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eCodecCallDirection.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eCodecCallDirection.cs new file mode 100644 index 00000000..a5e118df --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eCodecCallDirection.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.Codec + +{ + public enum eCodecCallDirection + { + Unknown = 0, Incoming, Outgoing + } + + public class CodecCallDirection + { + /// + /// Takes the Cisco call type and converts to the matching enum + /// + /// + /// + public static eCodecCallDirection ConvertToDirectionEnum(string s) + { + switch (s.ToLower()) + { + case "incoming": + { + return eCodecCallDirection.Incoming; + } + case "outgoing": + { + return eCodecCallDirection.Outgoing; + } + default: + return eCodecCallDirection.Unknown; + } + + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eCodecCallStatus.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eCodecCallStatus.cs new file mode 100644 index 00000000..610d928b --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eCodecCallStatus.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public enum eCodecCallStatus + { + Unknown = 0, + Connected, + Connecting, + Dialing, + Disconnected, + Disconnecting, + EarlyMedia, + Idle, + OnHold, + Ringing, + Preserved, + RemotePreserved, + } + + + public class CodecCallStatus + { + + /// + /// Takes the Cisco call type and converts to the matching enum + /// + /// + /// + public static eCodecCallStatus ConvertToStatusEnum(string s) + { + switch (s) + { + case "Connected": + { + return eCodecCallStatus.Connected; + } + case "Connecting": + { + return eCodecCallStatus.Connecting; + } + case "Dialling": + { + return eCodecCallStatus.Dialing; + } + case "Disconnected": + { + return eCodecCallStatus.Disconnected; + } + case "Disconnecting": + { + return eCodecCallStatus.Disconnecting; + } + case "EarlyMedia": + { + return eCodecCallStatus.EarlyMedia; + } + case "Idle": + { + return eCodecCallStatus.Idle; + } + case "OnHold": + { + return eCodecCallStatus.OnHold; + } + case "Ringing": + { + return eCodecCallStatus.Ringing; + } + case "Preserved": + { + return eCodecCallStatus.Preserved; + } + case "RemotePreserved": + { + return eCodecCallStatus.RemotePreserved; + } + default: + return eCodecCallStatus.Unknown; + } + + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eCodecCallType.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eCodecCallType.cs new file mode 100644 index 00000000..dbab015b --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eCodecCallType.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.Codec + +{ + public enum eCodecCallType + { + Unknown = 0, + Audio, + Video, + AudioCanEscalate, + ForwardAllCall + } + + public class CodecCallType + { + + /// + /// Takes the Cisco call type and converts to the matching enum + /// + /// + /// + public static eCodecCallType ConvertToTypeEnum(string s) + { + switch (s) + { + case "Audio": + { + return eCodecCallType.Audio; + } + case "Video": + { + return eCodecCallType.Video; + } + case "AudioCanEscalate": + { + return eCodecCallType.AudioCanEscalate; + } + case "ForwardAllCall": + { + return eCodecCallType.ForwardAllCall; + } + default: + return eCodecCallType.Unknown; + } + + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eMeetingPrivacy.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eMeetingPrivacy.cs new file mode 100644 index 00000000..f163a864 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/eMeetingPrivacy.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public enum eMeetingPrivacy + { + Unknown = 0, + Public, + Private + } + + public class CodecCallPrivacy + { + /// + /// Takes the Cisco privacy type and converts to the matching enum + /// + /// + /// + public static eMeetingPrivacy ConvertToDirectionEnum(string s) + { + switch (s.ToLower()) + { + case "public": + { + return eMeetingPrivacy.Public; + } + case "private": + { + return eMeetingPrivacy.Private; + } + default: + return eMeetingPrivacy.Unknown; + } + + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iCodecAudio.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iCodecAudio.cs new file mode 100644 index 00000000..d61f7d26 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iCodecAudio.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + /// + /// Defines minimum volume controls for a codec device with dialing capabilities + /// + public interface ICodecAudio : IBasicVolumeWithFeedback, IPrivacy + { + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallFavorites.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallFavorites.cs new file mode 100644 index 00000000..50c1b2c9 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallFavorites.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public interface IHasCallFavorites + { + CodecCallFavorites CallFavorites { get; } + } + + /// + /// Represents favorites entries for a codec device + /// + public class CodecCallFavorites + { + public List Favorites { get; set; } + + public CodecCallFavorites() + { + Favorites = new List(); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs new file mode 100644 index 00000000..256938d1 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasCallHistory.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public interface IHasCallHistory + { + CodecCallHistory CallHistory { get; } + + void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry); + } + + public enum eCodecOccurrenceType + { + Unknown = 0, + Placed, + Received, + NoAnswer + } + + /// + /// Represents the recent call history for a codec device + /// + public class CodecCallHistory + { + public event EventHandler RecentCallsListHasChanged; + + public List RecentCalls { get; private set; } + + /// + /// Item that gets added to the list when there are no recent calls in history + /// + CallHistoryEntry ListEmptyEntry; + + public CodecCallHistory() + { + ListEmptyEntry = new CallHistoryEntry() { Name = "No Recent Calls" }; + + RecentCalls = new List(); + + RecentCalls.Add(ListEmptyEntry); + } + + void OnRecentCallsListChange() + { + var handler = RecentCallsListHasChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + + public void RemoveEntry(CallHistoryEntry entry) + { + RecentCalls.Remove(entry); + OnRecentCallsListChange(); + } + + /// + /// Generic call history entry, not device specific + /// + public class CallHistoryEntry : CodecActiveCallItem + { + [JsonConverter(typeof(IsoDateTimeConverter))] + [JsonProperty("startTime")] + public DateTime StartTime { get; set; } + [JsonConverter(typeof(StringEnumConverter))] + [JsonProperty("occurrenceType")] + public eCodecOccurrenceType OccurrenceType { get; set; } + [JsonProperty("occurrenceHistoryId")] + public string OccurrenceHistoryId { get; set; } + } + + /// + /// Converts a list of call history entries returned by a Cisco codec to the generic list type + /// + /// + /// + public void ConvertCiscoCallHistoryToGeneric(List entries) + { + var genericEntries = new List(); + + foreach (CiscoCallHistory.Entry entry in entries) + { + + genericEntries.Add(new CallHistoryEntry() + { + Name = entry.DisplayName.Value, + Number = entry.CallbackNumber.Value, + StartTime = entry.LastOccurrenceStartTime.Value, + OccurrenceHistoryId = entry.LastOccurrenceHistoryId.Value, + OccurrenceType = ConvertToOccurenceTypeEnum(entry.OccurrenceType.Value) + }); + } + + // Check if list is empty and if so, add an item to display No Recent Calls + if(genericEntries.Count == 0) + genericEntries.Add(ListEmptyEntry); + + RecentCalls = genericEntries; + OnRecentCallsListChange(); + } + + /// + /// Takes the Cisco occurence type and converts it to the matching enum + /// + /// + /// Requirements for a device that has dialing capabilities + /// + public interface IHasDialer + { + // Add requirements for Dialer functionality + + event EventHandler CallStatusChange; + + void Dial(string number); + void EndCall(CodecActiveCallItem activeCall); + void EndAllCalls(); + void AcceptCall(CodecActiveCallItem item); + void RejectCall(CodecActiveCallItem item); + void SendDtmf(string digit); + + bool IsInCall { get; } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs new file mode 100644 index 00000000..3a984ab2 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasDirectory.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + /// + /// Defines the API for codecs with a directory + /// + public interface IHasDirectory + { + event EventHandler DirectoryResultReturned; + + CodecDirectory DirectoryRoot { get; } + + CodecDirectory CurrentDirectoryResult { get; } + + CodecPhonebookSyncState PhonebookSyncState { get; } + + void SearchDirectory(string searchString); + + void GetDirectoryFolderContents(string folderId); + + void SetCurrentDirectoryToRoot(); + + void GetDirectoryParentFolderContents(); + + BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; } + + /// + /// Tracks the directory browse history when browsing beyond the root directory + /// + List DirectoryBrowseHistory { get; } + } + + /// + /// + /// + public class DirectoryEventArgs : EventArgs + { + public CodecDirectory Directory { get; set; } + public bool DirectoryIsOnRoot { get; set; } + } + + /// + /// Represents a codec directory + /// + public class CodecDirectory + { + /// + /// Represents the contents of the directory + /// + [JsonProperty("directoryResults")] + public List CurrentDirectoryResults { get; private set; } + + /// + /// Used to store the ID of the current folder for CurrentDirectoryResults + /// + [JsonProperty("resultsFolderId")] + public string ResultsFolderId { get; set; } + + public CodecDirectory() + { + CurrentDirectoryResults = new List(); + } + + /// + /// Adds folders to the directory + /// + /// + public void AddFoldersToDirectory(List folders) + { + if(folders != null) + CurrentDirectoryResults.AddRange(folders); + + SortDirectory(); + } + + /// + /// Adds contacts to the directory + /// + /// + public void AddContactsToDirectory(List contacts) + { + if(contacts != null) + CurrentDirectoryResults.AddRange(contacts); + + SortDirectory(); + } + + /// + /// Sorts the DirectoryResults list to display all folders alphabetically, then all contacts alphabetically + /// + private void SortDirectory() + { + var sortedFolders = new List(); + + sortedFolders.AddRange(CurrentDirectoryResults.Where(f => f is DirectoryFolder)); + + sortedFolders.OrderBy(f => f.Name); + + var sortedContacts = new List(); + + sortedContacts.AddRange(CurrentDirectoryResults.Where(c => c is DirectoryContact)); + + sortedFolders.OrderBy(c => c.Name); + + CurrentDirectoryResults.Clear(); + + CurrentDirectoryResults.AddRange(sortedFolders); + + CurrentDirectoryResults.AddRange(sortedContacts); + } + + } + + /// + /// Used to decorate a contact to indicate it can be invided to a meeting + /// + public interface IInvitableContact + { + + } + + /// + /// Represents an item in the directory + /// + public class DirectoryItem : ICloneable + { + public object Clone() + { + return this.MemberwiseClone(); + } + + [JsonProperty("folderId")] + public string FolderId { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + } + + /// + /// Represents a folder type DirectoryItem + /// + public class DirectoryFolder : DirectoryItem + { + [JsonProperty("contacts")] + public List Contacts { get; set; } + + [JsonProperty("parentFolderId")] + public string ParentFolderId { get; set; } + + public DirectoryFolder() + { + Contacts = new List(); + } + } + + /// + /// Represents a contact type DirectoryItem + /// + public class DirectoryContact : DirectoryItem + { + [JsonProperty("contactId")] + public string ContactId { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("contactMethods")] + public List ContactMethods { get; set; } + + public DirectoryContact() + { + ContactMethods = new List(); + } + } + + /// + /// Represents a method of contact for a contact + /// + public class ContactMethod + { + [JsonProperty("contactMethodId")] + public string ContactMethodId { get; set; } + + [JsonProperty("number")] + public string Number { get; set; } + + [JsonProperty("device")] + [JsonConverter(typeof(StringEnumConverter))] + public eContactMethodDevice Device { get; set; } + + [JsonProperty("callType")] + [JsonConverter(typeof(StringEnumConverter))] + public eContactMethodCallType CallType { get; set; } + } + + /// + /// + /// + public enum eContactMethodDevice + { + Unknown = 0, + Mobile, + Other, + Telephone, + Video + } + + /// + /// + /// + public enum eContactMethodCallType + { + Unknown = 0, + Audio, + Video + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs new file mode 100644 index 00000000..5a006388 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public enum eMeetingEventChangeType + { + Unkown = 0, + MeetingStartWarning, + MeetingStart, + MeetingEndWarning, + MeetingEnd + } + + public interface IHasScheduleAwareness + { + CodecScheduleAwareness CodecSchedule { get; } + + void GetSchedule(); + } + + public class CodecScheduleAwareness + { + List _Meetings; + + public event EventHandler MeetingEventChange; + + public event EventHandler MeetingsListHasChanged; + + /// + /// Setter triggers MeetingsListHasChanged event + /// + public List Meetings + { + get + { + return _Meetings; + } + set + { + _Meetings = value; + + var handler = MeetingsListHasChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + } + + private CTimer ScheduleChecker; + + public CodecScheduleAwareness() + { + Meetings = new List(); + + ScheduleChecker = new CTimer(CheckSchedule, null, 1000, 1000); + } + + private void OnMeetingChange(eMeetingEventChangeType changeType, Meeting meeting) + { + var handler = MeetingEventChange; + if (handler != null) + { + handler(this, new MeetingEventArgs() { ChangeType = changeType, Meeting = meeting }); + } + } + + private void CheckSchedule(object o) + { + // Iterate the meeting list and check if any meeting need to do anythingk + + foreach (Meeting m in Meetings) + { + eMeetingEventChangeType changeType = eMeetingEventChangeType.Unkown; + + if (m.TimeToMeetingStart.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes) // Meeting is about to start + changeType = eMeetingEventChangeType.MeetingStartWarning; + else if (m.TimeToMeetingStart.TotalMinutes == 0) // Meeting Start + changeType = eMeetingEventChangeType.MeetingStart; + else if (m.TimeToMeetingEnd.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes) // Meeting is about to end + changeType = eMeetingEventChangeType.MeetingEndWarning; + else if (m.TimeToMeetingEnd.TotalMinutes == 0) // Meeting has ended + changeType = eMeetingEventChangeType.MeetingEnd; + + if (changeType != eMeetingEventChangeType.Unkown) + OnMeetingChange(changeType, m); + } + + + } + } + + /// + /// Generic class to represent a meeting (Cisco or Polycom OBTP or Fusion) + /// + public class Meeting + { + public TimeSpan MeetingWarningMinutes = TimeSpan.FromMinutes(5); + + public string Id { get; set; } + public string Organizer { get; set; } + public string Title { get; set; } + public string Agenda { get; set; } + public TimeSpan TimeToMeetingStart + { + get + { + return StartTime - DateTime.Now; + } + } + public TimeSpan TimeToMeetingEnd + { + get + { + return EndTime - DateTime.Now; + } + } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + public TimeSpan Duration + { + get + { + return EndTime - StartTime; + } + } + public eMeetingPrivacy Privacy { get; set; } + public bool Joinable + { + get + { + return StartTime.AddMinutes(-5) <= DateTime.Now + && DateTime.Now <= EndTime; //.AddMinutes(-5); + } + } + //public string ConferenceNumberToDial { get; set; } + public string ConferencePassword { get; set; } + public bool IsOneButtonToPushMeeting { get; set; } + + public List Calls { get; private set; } + + public Meeting() + { + Calls = new List(); + } + } + + public class Call + { + public string Number { get; set; } + public string Protocol { get; set; } + public string CallRate { get; set; } + public string CallType { get; set; } + } + + public class MeetingEventArgs : EventArgs + { + public eMeetingEventChangeType ChangeType { get; set; } + public Meeting Meeting { get; set; } + } + +} diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Crestron/Gateways/CenRfgwController.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Crestron/Gateways/CenRfgwController.cs new file mode 100644 index 00000000..ee37674a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Crestron/Gateways/CenRfgwController.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.Gateways; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common +{ + public class CenRfgwController : CrestronGenericBaseDevice + { + public GatewayBase Gateway { get { return Hardware as GatewayBase; } } + + /// + /// Constructor for the on-board gateway + /// + /// + /// + public CenRfgwController(string key, string name, GatewayBase gateway) : + base(key, name, gateway) + { + } + + public static CenRfgwController GetNewExGatewayController(string key, string name, string id, string gatewayType) + { + uint uid; + eExGatewayType type; + try + { + uid = Convert.ToUInt32(id, 16); + type = (eExGatewayType)Enum.Parse(typeof(eExGatewayType), gatewayType, true); + var cs = Global.ControlSystem; + + GatewayBase gw = null; + switch (type) + { + case eExGatewayType.Ethernet: + gw = new CenRfgwEx(uid, cs); + break; + case eExGatewayType.EthernetShared: + gw = new CenRfgwExEthernetSharable(uid, cs); + break; + case eExGatewayType.Cresnet: + gw = new CenRfgwExCresnet(uid, cs); + break; + } + return new CenRfgwController(key, name, gw); + } + catch (Exception) + { + Debug.Console(0, "ERROR: Cannot create EX Gateway, id {0}, type {1}", id, gatewayType); + return null; + } + } + public static CenRfgwController GetNewErGatewayController(string key, string name, string id, string gatewayType) + { + uint uid; + eExGatewayType type; + try + { + uid = Convert.ToUInt32(id, 16); + type = (eExGatewayType)Enum.Parse(typeof(eExGatewayType), gatewayType, true); + var cs = Global.ControlSystem; + + GatewayBase gw = null; + switch (type) + { + case eExGatewayType.Ethernet: + gw = new CenErfgwPoe(uid, cs); + break; + case eExGatewayType.EthernetShared: + gw = new CenErfgwPoeEthernetSharable(uid, cs); + break; + case eExGatewayType.Cresnet: + gw = new CenErfgwPoeCresnet(uid, cs); + break; + } + return new CenRfgwController(key, name, gw); + } + catch (Exception) + { + Debug.Console(0, "ERROR: Cannot create EX Gateway, id {0}, type {1}", id, gatewayType); + return null; + } + } + + + /// + /// Gets the actual Crestron EX gateway for a given key. "processor" or the key of + /// a CenRfgwExController in DeviceManager + /// + /// + /// Either processor GW or Gateway property of CenRfgwExController + public static GatewayBase GetExGatewayBaseForKey(string key) + { + key = key.ToLower(); + if (key == "processor" && Global.ControlSystem.SupportsInternalRFGateway) + return Global.ControlSystem.ControllerRFGatewayDevice; + var gwCont = DeviceManager.GetDeviceForKey(key) as CenRfgwController; + if (gwCont != null) + return gwCont.Gateway; + + return null; + } + } + + public enum eExGatewayType + { + Ethernet, EthernetShared, Cresnet + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraForteDsp.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraForteDsp.cs new file mode 100644 index 00000000..27a29145 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraForteDsp.cs @@ -0,0 +1,376 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Text.RegularExpressions; + + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + + // QUESTIONS: + // + // When subscribing, just use the Instance ID for Custom Name? + + // Verbose on subscriptions? + + // Example subscription feedback responses + // ! "publishToken":"name" "value":-77.0 + // ! "myLevelName" -77 + + public class BiampTesiraForteDsp : DspBase + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + + new public Dictionary LevelControlPoints { get; private set; } + + public bool isSubscribed; + + private CTimer SubscriptionTimer; + + CrestronQueue CommandQueue; + + bool CommandQueueInProgress = false; + + //new public Dictionary DialerControlPoints { get; private set; } + + //new public Dictionary SwitcherControlPoints { get; private set; } + + /// + /// Shows received lines as hex + /// + public bool ShowHexResponse { get; set; } + + public BiampTesiraForteDsp(string key, string name, IBasicCommunication comm, BiampTesiraFortePropertiesConfig props) : + base(key, name) + { + CommandQueue = new CrestronQueue(100); + + Communication = comm; + var socket = comm as ISocketStatus; + if (socket != null) + { + // This instance uses IP control + + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + else + { + // This instance uses RS-232 control + } + PortGather = new CommunicationGather(Communication, "\x0d\x0a"); + PortGather.LineReceived += this.Port_LineReceived; + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { +//#warning Need to deal with this poll string + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 120000, 120000, 300000, "SESSION get aliases\x0d\x0a"); + } + + LevelControlPoints = new Dictionary(); + + foreach (KeyValuePair block in props.LevelControlBlocks) + { + this.LevelControlPoints.Add(block.Key, new TesiraForteLevelControl(block.Key, block.Value, this)); + } + + } + + public override bool CustomActivate() + { + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + + CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + return true; + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString()); + + if (e.Client.IsConnected) + { + // Tasks on connect + } + else + { + // Cleanup items from this session + + if (SubscriptionTimer != null) + { + SubscriptionTimer.Stop(); + SubscriptionTimer = null; + } + + isSubscribed = false; + CommandQueue.Clear(); + CommandQueueInProgress = false; + } + } + + /// + /// Initiates the subscription process to the DSP + /// + void SubscribeToAttributes() + { + SendLine("SESSION set verbose true"); + + foreach (KeyValuePair level in LevelControlPoints) + { + level.Value.Subscribe(); + } + + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + + ResetSubscriptionTimer(); + } + + /// + /// Resets or Sets the subscription timer + /// + void ResetSubscriptionTimer() + { + isSubscribed = true; + + if (SubscriptionTimer != null) + { + SubscriptionTimer = new CTimer(o => SubscribeToAttributes(), 30000); + SubscriptionTimer.Reset(); + + } + } + + /// + /// Handles a response message from the DSP + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + if (Debug.Level == 2) + Debug.Console(2, this, "RX: '{0}'", + ShowHexResponse ? ComTextHelper.GetEscapedText(args.Text) : args.Text); + + Debug.Console(1, this, "RX: '{0}'", args.Text); + + try + { + if (args.Text.IndexOf("Welcome to the Tesira Text Protocol Server...") > -1) + { + // Indicates a new TTP session + + SubscribeToAttributes(); + } + else if (args.Text.IndexOf("publishToken") > -1) + { + // response is from a subscribed attribute + + string pattern = "! \"publishToken\":[\"](.*)[\"] \"value\":(.*)"; + + Match match = Regex.Match(args.Text, pattern); + + if (match.Success) + { + + string key; + + string customName; + + string value; + + customName = match.Groups[1].Value; + + // Finds the key (everything before the '~' character + key = customName.Substring(0, customName.IndexOf("~", 0) - 1); + + value = match.Groups[2].Value; + + foreach (KeyValuePair controlPoint in LevelControlPoints) + { + if (customName == controlPoint.Value.LevelCustomName || customName == controlPoint.Value.MuteCustomName) + { + controlPoint.Value.ParseSubscriptionMessage(customName, value); + return; + } + + } + } + + /// same for dialers + /// same for switchers + + } + else if (args.Text.IndexOf("+OK") > -1) + { + if (args.Text == "+OK" || args.Text.IndexOf("list\":") > -1 ) // Check for a simple "+OK" only 'ack' repsonse or a list response and ignore + return; + + // response is not from a subscribed attribute. From a get/set/toggle/increment/decrement command + + if (!CommandQueue.IsEmpty) + { + if (CommandQueue.Peek() is QueuedCommand) + { + // Expected response belongs to a child class + QueuedCommand tempCommand = (QueuedCommand)CommandQueue.TryToDequeue(); + //Debug.Console(1, this, "Command Dequeued. CommandQueue Size: {0}", CommandQueue.Count); + + tempCommand.ControlPoint.ParseGetMessage(tempCommand.AttributeCode, args.Text); + } + else + { + // Expected response belongs to this class + string temp = (string)CommandQueue.TryToDequeue(); + //Debug.Console(1, this, "Command Dequeued. CommandQueue Size: {0}", CommandQueue.Count); + + } + + if (CommandQueue.IsEmpty) + CommandQueueInProgress = false; + else + SendNextQueuedCommand(); + + } + + + } + else if (args.Text.IndexOf("-ERR") > -1) + { + // Error response + + switch (args.Text) + { + case "-ERR ALREADY_SUBSCRIBED": + { + ResetSubscriptionTimer(); + break; + } + default: + { + Debug.Console(0, this, "Error From DSP: '{0}'", args.Text); + break; + } + } + + } + } + catch (Exception e) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Error parsing response: '{0}'\n{1}", args.Text, e); + } + + } + + /// + /// Sends a command to the DSP (with delimiter appended) + /// + /// Command to send + public void SendLine(string s) + { + Debug.Console(1, this, "TX: '{0}'", s); + Communication.SendText(s + "\x0a"); + } + + /// + /// Adds a command from a child module to the queue + /// + /// Command object from child module + public void EnqueueCommand(QueuedCommand commandToEnqueue) + { + CommandQueue.Enqueue(commandToEnqueue); + //Debug.Console(1, this, "Command (QueuedCommand) Enqueued '{0}'. CommandQueue has '{1}' Elements.", commandToEnqueue.Command, CommandQueue.Count); + + if(!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Adds a raw string command to the queue + /// + /// + public void EnqueueCommand(string command) + { + CommandQueue.Enqueue(command); + //Debug.Console(1, this, "Command (string) Enqueued '{0}'. CommandQueue has '{1}' Elements.", command, CommandQueue.Count); + + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Sends the next queued command to the DSP + /// + void SendNextQueuedCommand() + { + //Debug.Console(2, this, "Attempting to send next queued command. CommandQueueInProgress: {0} Communication isConnected: {1}", CommandQueueInProgress, Communication.IsConnected); + + //if (CommandQueue.IsEmpty) + // CommandQueueInProgress = false; + + //Debug.Console(1, this, "CommandQueue has {0} Elements:\n", CommandQueue.Count); + + //foreach (object o in CommandQueue) + //{ + // if (o is string) + // Debug.Console(1, this, "{0}", o); + // else if(o is QueuedCommand) + // { + // var item = (QueuedCommand)o; + // Debug.Console(1, this, "{0}", item.Command); + // } + //} + + //Debug.Console(1, this, "End of CommandQueue"); + + if (Communication.IsConnected && !CommandQueue.IsEmpty) + { + CommandQueueInProgress = true; + + if (CommandQueue.Peek() is QueuedCommand) + { + QueuedCommand nextCommand = new QueuedCommand(); + + nextCommand = (QueuedCommand)CommandQueue.Peek(); + + SendLine(nextCommand.Command); + } + else + { + string nextCommand = (string)CommandQueue.Peek(); + + SendLine(nextCommand); + } + } + + } + + /// + /// Sends a command to execute a preset + /// + /// Preset Name + public override void RunPreset(string name) + { + SendLine(string.Format("DEVICE recallPreset {0}", name)); + } + + public class QueuedCommand + { + public string Command { get; set; } + public string AttributeCode { get; set; } + public TesiraForteControlPoint ControlPoint { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraForteDspLevel.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraForteDspLevel.cs new file mode 100644 index 00000000..d0f83115 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraForteDspLevel.cs @@ -0,0 +1,366 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Text.RegularExpressions; + + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + public class TesiraForteLevelControl : TesiraForteControlPoint, IBiampTesiraDspLevelControl, IKeyed + { + bool _IsMuted; + ushort _VolumeLevel; + + public BoolFeedback MuteFeedback { get; private set; } + + public IntFeedback VolumeLevelFeedback { get; private set; } + + + public bool Enabled { get; set; } + public string ControlPointTag { get { return base.InstanceTag; } } + + /// + /// Used for to identify level subscription values + /// + public string LevelCustomName { get; private set; } + + /// + /// Used for to identify mute subscription values + /// + public string MuteCustomName { get; private set; } + + /// + /// Minimum fader level + /// + double MinLevel; + + /// + /// Maximum fader level + /// + double MaxLevel; + + /// + /// Checks if a valid subscription string has been recieved for all subscriptions + /// + public bool IsSubsribed + { + get + { + bool isSubscribed = false; + + if (HasMute && MuteIsSubscribed) + isSubscribed = true; + + if (HasLevel && LevelIsSubscribed) + isSubscribed = true; + + return isSubscribed; + } + } + + public bool AutomaticUnmuteOnVolumeUp { get; private set; } + + public bool HasMute { get; private set; } + + public bool HasLevel { get; private set; } + + bool MuteIsSubscribed; + + bool LevelIsSubscribed; + + //public TesiraForteLevelControl(string label, string id, int index1, int index2, bool hasMute, bool hasLevel, BiampTesiraForteDsp parent) + // : base(id, index1, index2, parent) + //{ + // Initialize(label, hasMute, hasLevel); + //} + + public TesiraForteLevelControl(string key, BiampTesiraForteLevelControlBlockConfig config, BiampTesiraForteDsp parent) + : base(config.InstanceTag, config.Index1, config.Index2, parent) + { + Initialize(key, config.Label, config.HasMute, config.HasLevel); + } + + + /// + /// Initializes this attribute based on config values and generates subscriptions commands and adds commands to the parent's queue. + /// + public void Initialize(string key, string label, bool hasMute, bool hasLevel) + { + Key = string.Format("{0}--{1}", Parent.Key, key); + + DeviceManager.AddDevice(this); + + Debug.Console(2, this, "Adding LevelControl '{0}'", Key); + + this.IsSubscribed = false; + + MuteFeedback = new BoolFeedback(() => _IsMuted); + + VolumeLevelFeedback = new IntFeedback(() => _VolumeLevel); + + HasMute = hasMute; + HasLevel = hasLevel; + } + + public void Subscribe() + { + // Do subscriptions and blah blah + + // Subscribe to mute + if (this.HasMute) + { + MuteCustomName = string.Format("{0}~mute{1}", this.InstanceTag, this.Index1); + + SendSubscriptionCommand(MuteCustomName, "mute", 500); + } + + // Subscribe to level + if (this.HasLevel) + { + LevelCustomName = string.Format("{0}~level{1}", this.InstanceTag, this.Index1); + + SendSubscriptionCommand(LevelCustomName, "level", 250); + + SendFullCommand("get", "minLevel", null); + + SendFullCommand("get", "maxLevel", null); + } + } + + + /// + /// Parses the response from the DspBase + /// + /// + /// + public void ParseSubscriptionMessage(string customName, string value) + { + + // Check for valid subscription response + + if (this.HasMute && customName == MuteCustomName) + { + //if (value.IndexOf("+OK") > -1) + //{ + // int pointer = value.IndexOf(" +OK"); + + // MuteIsSubscribed = true; + + // // Removes the +OK + // value = value.Substring(0, value.Length - (value.Length - (pointer - 1))); + //} + + if (value.IndexOf("true") > -1) + { + _IsMuted = true; + MuteIsSubscribed = true; + + } + else if (value.IndexOf("false") > -1) + { + _IsMuted = false; + MuteIsSubscribed = true; + } + + MuteFeedback.FireUpdate(); + } + else if (this.HasLevel && customName == LevelCustomName) + { + //if (value.IndexOf("+OK") > -1) + //{ + // int pointer = value.IndexOf(" +OK"); + + // LevelIsSubscribed = true; + + //} + + var _value = Double.Parse(value); + + _VolumeLevel = (ushort)Scale(_value, MinLevel, MaxLevel, 0, 65535); + + LevelIsSubscribed = true; + + VolumeLevelFeedback.FireUpdate(); + } + + } + + /// + /// Parses a non subscription response + /// + /// The attribute code of the command + /// The message to parse + public override void ParseGetMessage(string attributeCode, string message) + { + try + { + // Parse an "+OK" message + string pattern = @"\+OK ""value"":(.*)"; + + Match match = Regex.Match(message, pattern); + + if (match.Success) + { + + string value = match.Groups[1].Value; + + Debug.Console(1, this, "Response: '{0}' Value: '{1}'", attributeCode, value); + + if (message.IndexOf("\"value\":") > -1) + { + switch (attributeCode) + { + case "minLevel": + { + MinLevel = Double.Parse(value); + + Debug.Console(1, this, "MinLevel is '{0}'", MinLevel); + + break; + } + case "maxLevel": + { + MaxLevel = Double.Parse(value); + + Debug.Console(1, this, "MaxLevel is '{0}'", MaxLevel); + + break; + } + default: + { + Debug.Console(2, "Response does not match expected attribute codes: '{0}'", message); + + break; + } + } + } + } + } + catch (Exception e) + { + Debug.Console(2, "Unable to parse message: '{0}'\n{1}", message, e); + } + + } + + /// + /// Turns the mute off + /// + public void MuteOff() + { + SendFullCommand("set", "mute", "false"); + } + + /// + /// Turns the mute on + /// + public void MuteOn() + { + SendFullCommand("set", "mute", "true"); + } + + /// + /// Sets the volume to a specified level + /// + /// + public void SetVolume(ushort level) + { + Debug.Console(1, this, "volume: {0}", level); + // Unmute volume if new level is higher than existing + if (level > _VolumeLevel && AutomaticUnmuteOnVolumeUp) + if(_IsMuted) + MuteOff(); + + double volumeLevel = Scale(level, 0, 65535, MinLevel, MaxLevel); + + SendFullCommand("set", "level", string.Format("{0:0.000000}", volumeLevel)); + } + + /// + /// Toggles mute status + /// + public void MuteToggle() + { + SendFullCommand("toggle", "mute", ""); + } + + /// + /// Decrements volume level + /// + /// + public void VolumeDown(bool pressRelease) + { + SendFullCommand("decrement", "level", ""); + } + + /// + /// Increments volume level + /// + /// + public void VolumeUp(bool pressRelease) + { + SendFullCommand("increment", "level", ""); + + if (AutomaticUnmuteOnVolumeUp) + if (!_IsMuted) + MuteOff(); + } + + ///// + ///// Scales the input from the input range to the output range + ///// + ///// + ///// + ///// + ///// + ///// + ///// + //int Scale(int input, int inMin, int inMax, int outMin, int outMax) + //{ + // Debug.Console(1, this, "Scaling (int) input '{0}' with min '{1}'/max '{2}' to output range min '{3}'/max '{4}'", input, inMin, inMax, outMin, outMax); + + // int inputRange = inMax - inMin; + + // int outputRange = outMax - outMin; + + // var output = (((input-inMin) * outputRange) / inputRange ) - outMin; + + // Debug.Console(1, this, "Scaled output '{0}'", output); + + // return output; + //} + + /// + /// Scales the input from the input range to the output range + /// + /// + /// + /// + /// + /// + /// + double Scale(double input, double inMin, double inMax, double outMin, double outMax) + { + Debug.Console(1, this, "Scaling (double) input '{0}' with min '{1}'/max '{2}' to output range min '{3}'/max '{4}'",input ,inMin ,inMax ,outMin, outMax); + + double inputRange = inMax - inMin; + + if (inputRange <= 0) + { + throw new ArithmeticException(string.Format("Invalid Input Range '{0}' for Scaling. Min '{1}' Max '{2}'.", inputRange, inMin, inMax)); + } + + double outputRange = outMax - outMin; + + var output = (((input - inMin) * outputRange) / inputRange) + outMin; + + Debug.Console(1, this, "Scaled output '{0}'", output); + + return output; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraFortePropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraFortePropertiesConfig.cs new file mode 100644 index 00000000..1b4ae8a1 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraFortePropertiesConfig.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + /// + /// + /// + public class BiampTesiraFortePropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public ControlPropertiesConfig Control { get; set; } + + /// + /// These are key-value pairs, string id, string type. + /// Valid types are level and mute. + /// Need to include the index values somehow + /// + public Dictionary LevelControlBlocks { get; set; } + // public Dictionary DialerControlBlocks {get; set;} + } + + public class BiampTesiraForteLevelControlBlockConfig + { + public bool Enabled { get; set; } + public string Label { get; set; } + public string InstanceTag { get; set; } + public int Index1 { get; set; } + public int Index2 { get; set; } + public bool HasMute { get; set; } + public bool HasLevel { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/TesiraForteControlPoint.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/TesiraForteControlPoint.cs new file mode 100644 index 00000000..a509be2a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/TesiraForteControlPoint.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + public abstract class TesiraForteControlPoint : DspControlPoint + { + public string Key { get; protected set; } + + public string InstanceTag { get; set; } + public int Index1 { get; private set; } + public int Index2 { get; private set; } + public BiampTesiraForteDsp Parent { get; private set; } + + public bool IsSubscribed { get; protected set; } + + protected TesiraForteControlPoint(string id, int index1, int index2, BiampTesiraForteDsp parent) + { + InstanceTag = id; + Index1 = index1; + Index2 = index2; + Parent = parent; + } + + virtual public void Initialize() + { + } + + /// + /// Sends a command to the DSP + /// + /// command + /// attribute code + /// value (use "" if not applicable) + public virtual void SendFullCommand(string command, string attributeCode, string value) + { + // Command Format: InstanceTag get/set/toggle/increment/decrement/subscribe/unsubscribe attributeCode [index] [value] + // Ex: "RoomLevel set level 1.00" + + string cmd; + + if (attributeCode == "level" || attributeCode == "mute" || attributeCode == "minLevel" || attributeCode == "maxLevel" || attributeCode == "label" || attributeCode == "rampInterval" || attributeCode == "rampStep") + { + //Command requires Index + + if (String.IsNullOrEmpty(value)) + { + // format command without value + cmd = string.Format("{0} {1} {2} {3}", InstanceTag, command, attributeCode, Index1); + } + else + { + // format commadn with value + cmd = string.Format("{0} {1} {2} {3} {4}", InstanceTag, command, attributeCode, Index1, value); + } + + } + else + { + //Command does not require Index + + if (String.IsNullOrEmpty(value)) + { + cmd = string.Format("{0} {1} {2} {3}", InstanceTag, command, attributeCode, value); + } + else + { + cmd = string.Format("{0} {1} {2}", InstanceTag, command, attributeCode); + } + } + + if (command == "get") + { + // This command will generate a return value response so it needs to be queued + Parent.EnqueueCommand(new BiampTesiraForteDsp.QueuedCommand{ Command = cmd, AttributeCode = attributeCode, ControlPoint = this }); + } + else + { + // This command will generate a simple "+OK" response and doesn't need to be queued + Parent.SendLine(cmd); + } + + } + + virtual public void ParseGetMessage(string attributeCode, string message) + { + } + + + + public virtual void SendSubscriptionCommand(string customName, string attributeCode, int responseRate) + { + // Subscription string format: InstanceTag subscribe attributeCode Index1 customName responseRate + // Ex: "RoomLevel subscribe level 1 MyRoomLevel 500" + + string cmd; + + if (responseRate > 0) + { + cmd = string.Format("{0} subscribe {1} {2} {3} {4}", InstanceTag, attributeCode, Index1, customName, responseRate); + } + else + { + cmd = string.Format("{0} subscribe {1} {2} {3}", InstanceTag, attributeCode, Index1, customName); + } + + Parent.SendLine(cmd); + } + + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/TesiraForteMuteControl.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/TesiraForteMuteControl.cs new file mode 100644 index 00000000..82689408 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/TesiraForteMuteControl.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + + //QUESTIONS: + + //When subscribing, just use the Instance ID for Custom Name? + + //Verbose on subscriptions? + + //! "publishToken":"name" "value":-77.0 + //! "myLevelName" -77 + + //public class TesiraForteMuteControl : IDspLevelControl + //{ + // BiampTesiraForteDsp Parent; + // bool _IsMuted; + // ushort _VolumeLevel; + + // public TesiraForteMuteControl(string id, BiampTesiraForteDsp parent) + // : base(id) + // { + // Parent = parent; + // } + + // public void Initialize() + // { + + // } + + // protected override Func MuteFeedbackFunc + // { + // get { return () => _IsMuted; } + // } + + // protected override Func VolumeLevelFeedbackFunc + // { + // get { return () => _VolumeLevel; } + // } + + // public override void MuteOff() + // { + // throw new NotImplementedException(); + // } + + // public override void MuteOn() + // { + // throw new NotImplementedException(); + // } + + // public override void SetVolume(ushort level) + // { + // throw new NotImplementedException(); + // } + + // public override void MuteToggle() + // { + // } + + // public override void VolumeDown(bool pressRelease) + // { + // throw new NotImplementedException(); + // } + + // public override void VolumeUp(bool pressRelease) + // { + // throw new NotImplementedException(); + // } + //} +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/DspBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/DspBase.cs new file mode 100644 index 00000000..36c655d2 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/DspBase.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + public abstract class DspBase : Device + { + public Dictionary LevelControlPoints { get; private set; } + + public Dictionary DialerControlPoints { get; private set; } + + public Dictionary SwitcherControlPoints { get; private set; } + + public abstract void RunPreset(string name); + + public DspBase(string key, string name) : + base(key, name) { } + + + // in audio call feedback + + // VOIP + // Phone dialer + } + + // Fusion + // Privacy state + // Online state + // level/mutes ? + + // AC Log call stats + + // Typical presets: + // call default preset to restore levels and mutes + + public abstract class DspControlPoint + { + string Key { get; set; } + } + + + public class DspDialerBase + { + + } + + + // Main program + // VTC + // ATC + // Mics, unusual + + public interface IBiampTesiraDspLevelControl : IBasicVolumeWithFeedback + { + /// + /// In BiAmp: Instance Tag, QSC: Named Control, Polycom: + /// + string ControlPointTag { get; } +#warning I dont think index1 and index2 should be a part of the interface. JTA 2018-07-17 + int Index1 { get; } + int Index2 { get; } + bool HasMute { get; } + bool HasLevel { get; } + bool AutomaticUnmuteOnVolumeUp { get; } + } + + + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/PolycomSoundStructure/SoundStructureBasics.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/PolycomSoundStructure/SoundStructureBasics.cs new file mode 100644 index 00000000..92af6a97 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/PolycomSoundStructure/SoundStructureBasics.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + public class SoundStructureBasics + { + // public Dictionary Levels { get; private set; } + + + } + + + +} + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDsp.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDsp.cs new file mode 100644 index 00000000..24fd93d4 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDsp.cs @@ -0,0 +1,328 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; +using System.Text.RegularExpressions; +using Crestron.SimplSharp.Reflection; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + + // QUESTIONS: + // + // When subscribing, just use the Instance ID for Custom Name? + + // Verbose on subscriptions? + + // Example subscription feedback responses + // ! "publishToken":"name" "value":-77.0 + // ! "myLevelName" -77 + + public class QscDsp : DspBase + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public GenericCommunicationMonitor CommunicationMonitor { get; private set; } + + new public Dictionary LevelControlPoints { get; private set; } + new public Dictionary Dialers { get; set; } + public List PresetList = new List(); + + public bool isSubscribed; + + + + CrestronQueue CommandQueue; + + bool CommandQueueInProgress = false; + + + public bool ShowHexResponse { get; set; } + + public QscDsp(string key, string name, IBasicCommunication comm, QscDspPropertiesConfig props) : + base(key, name) + { + CommandQueue = new CrestronQueue(100); + + Communication = comm; + var socket = comm as ISocketStatus; + if (socket != null) + { + // This instance uses IP control + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + else + { + // This instance uses RS-232 control + } + PortGather = new CommunicationGather(Communication, "\x0a"); + PortGather.LineReceived += this.Port_LineReceived; + + LevelControlPoints = new Dictionary(); + Dialers = new Dictionary(); + + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 20000, 120000, 300000, "cgp 1\x0D\x0A"); + } + + + foreach (KeyValuePair block in props.LevelControlBlocks) + { + this.LevelControlPoints.Add(block.Key, new QscDspLevelControl(block.Key, block.Value, this)); + Debug.Console(2, this, "Added LevelControlPoint {0}", block.Key); + + + } + foreach (KeyValuePair preset in props.presets) + { + this.addPreset(preset.Value); + Debug.Console(2, this, "Added Preset {0} {1}", preset.Value.label, preset.Value.preset); + } + foreach (KeyValuePair dialerConfig in props.dialerControlBlocks) + { + Debug.Console(2, this, "Added Dialer {0}\n {1}", dialerConfig.Key, dialerConfig.Value); + this.Dialers.Add(dialerConfig.Key, new QscDspDialer(dialerConfig.Value, this)); + + } + + } + + public override bool CustomActivate() + { + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + + + CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + return true; + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString()); + + if (e.Client.IsConnected) + { + SubscribeToAttributes(); + } + else + { + // Cleanup items from this session + CommandQueue.Clear(); + CommandQueueInProgress = false; + } + } + + /// + /// Initiates the subscription process to the DSP + /// + void SubscribeToAttributes() + { + SendLine("cgd 1"); + + SendLine("cgc 1"); + + foreach (KeyValuePair level in LevelControlPoints) + { + level.Value.Subscribe(); + } + + foreach (var dialer in Dialers) + { + dialer.Value.Subscribe(); + } + + if (CommunicationMonitor != null) + { + + CommunicationMonitor.Start(); + //CommunicationMonitor = null; + } + else + { + + } + //CommunicationMonitor.Message = "cgp 1\x0D\x0A"; + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + //CommunicationMonitor.Start(); + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + + // ResetSubscriptionTimer(); + } + + + + /// + /// Handles a response message from the DSP + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + Debug.Console(2, this, "RX: '{0}'", args.Text); + try + { + if (args.Text.IndexOf("sr ") > -1) + { + } + else if (args.Text.IndexOf("cv") > -1) + { + + var changeMessage = args.Text.Split(null); + + string changedInstance = changeMessage[1].Replace("\"", ""); + Debug.Console(1, this, "cv parse Instance: {0}", changedInstance); + bool foundItFlag = false; + foreach (KeyValuePair controlPoint in LevelControlPoints) + { + if (changedInstance == controlPoint.Value.LevelInstanceTag) + { + controlPoint.Value.ParseSubscriptionMessage(changedInstance, changeMessage[4]); + foundItFlag = true; + return; + } + else if (changedInstance == controlPoint.Value.MuteInstanceTag) + { + controlPoint.Value.ParseSubscriptionMessage(changedInstance, changeMessage[2].Replace("\"", "")); + foundItFlag = true; + return; + } + + } + if (!foundItFlag) + { + foreach (var dialer in Dialers) + { + PropertyInfo[] properties = dialer.Value.Tags.GetType().GetCType().GetProperties(); + //GetPropertyValues(Tags); + foreach (var prop in properties) + { + var propValue = prop.GetValue(dialer.Value.Tags, null) as string; + if (changedInstance == propValue) + { + dialer.Value.ParseSubscriptionMessage(changedInstance, changeMessage[2].Replace("\"", "")); + foundItFlag = true; + return; + } + } + if (foundItFlag) + { + return; + } + } + } + + } + + + } + catch (Exception e) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Error parsing response: '{0}'\n{1}", args.Text, e); + } + + } + + /// + /// Sends a command to the DSP (with delimiter appended) + /// + /// Command to send + public void SendLine(string s) + { + Debug.Console(1, this, "TX: '{0}'", s); + Communication.SendText(s + "\x0a"); + } + + /// + /// Adds a command from a child module to the queue + /// + /// Command object from child module + public void EnqueueCommand(QueuedCommand commandToEnqueue) + { + CommandQueue.Enqueue(commandToEnqueue); + //Debug.Console(1, this, "Command (QueuedCommand) Enqueued '{0}'. CommandQueue has '{1}' Elements.", commandToEnqueue.Command, CommandQueue.Count); + + if(!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Adds a raw string command to the queue + /// + /// + public void EnqueueCommand(string command) + { + CommandQueue.Enqueue(command); + //Debug.Console(1, this, "Command (string) Enqueued '{0}'. CommandQueue has '{1}' Elements.", command, CommandQueue.Count); + + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Sends the next queued command to the DSP + /// + void SendNextQueuedCommand() + { + if (Communication.IsConnected && !CommandQueue.IsEmpty) + { + CommandQueueInProgress = true; + + if (CommandQueue.Peek() is QueuedCommand) + { + QueuedCommand nextCommand = new QueuedCommand(); + + nextCommand = (QueuedCommand)CommandQueue.Peek(); + + SendLine(nextCommand.Command); + } + else + { + string nextCommand = (string)CommandQueue.Peek(); + + SendLine(nextCommand); + } + } + + } + + public void RunPresetNumber(ushort n) + { + RunPreset(PresetList[n].preset); + } + + public void addPreset(QscDspPresets s) + { + PresetList.Add(s); + } + /// + /// Sends a command to execute a preset + /// + /// Preset Name + public override void RunPreset(string name) + { + SendLine(string.Format("ssl {0}", name)); + SendLine("cgp 1"); + } + + public class QueuedCommand + { + public string Command { get; set; } + public string AttributeCode { get; set; } + public QscDspControlPoint ControlPoint { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspControlPoint.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspControlPoint.cs new file mode 100644 index 00000000..542336eb --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspControlPoint.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + public abstract class QscDspControlPoint : DspControlPoint + { + public string Key { get; protected set; } + + public string LevelInstanceTag { get; set; } + public string MuteInstanceTag { get; set; } + public QscDsp Parent { get; private set; } + + public bool IsSubscribed { get; protected set; } + + protected QscDspControlPoint(string levelInstanceTag, string muteInstanceTag, QscDsp parent) + { + LevelInstanceTag = levelInstanceTag; + MuteInstanceTag = muteInstanceTag; + Parent = parent; + } + + virtual public void Initialize() + { + } + + /// + /// Sends a command to the DSP + /// + /// command + /// attribute code + /// value (use "" if not applicable) + public virtual void SendFullCommand(string cmd, string instance, string value) + { + + var cmdToSemd = string.Format("{0} {1} {2}", cmd, instance, value); + + Parent.SendLine(cmdToSemd); + + } + + virtual public void ParseGetMessage(string attributeCode, string message) + { + } + + + + public virtual void SendSubscriptionCommand(string instanceTag, string changeGroup) + { + // Subscription string format: InstanceTag subscribe attributeCode Index1 customName responseRate + // Ex: "RoomLevel subscribe level 1 MyRoomLevel 500" + + string cmd; + + cmd = string.Format("cga {0} {1}", changeGroup, instanceTag); + + Parent.SendLine(cmd); + } + + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspDialer.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspDialer.cs new file mode 100644 index 00000000..387dffe7 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspDialer.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using Crestron.SimplSharp.Reflection; +using Crestron.SimplSharp; +using PepperDash.Essentials.Devices.Common.Codec; +using Crestron.SimplSharpPro.CrestronThread; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + public class QscDspDialer : DspDialerBase, IHasDialer + { + public QscDialerConfig Tags; + public bool IsInCall { get; private set; } + public QscDsp Parent { get; private set; } + public string DialString { get; private set; } + public bool OffHook { get; private set; } + public bool AutoAnswerState { get; private set; } + public bool DoNotDisturbState { get; private set; } + + public BoolFeedback OffHookFeedback; + public BoolFeedback AutoAnswerFeedback; + public BoolFeedback DoNotDisturbFeedback; + public StringFeedback DialStringFeedback; + + // Add requirements for Dialer functionality + + public QscDspDialer(QscDialerConfig Config, QscDsp parent) + { + Tags = Config; + Parent = parent; + DialStringFeedback = new StringFeedback(() => { return DialString; }); + OffHookFeedback = new BoolFeedback(() => { return OffHook; }); + AutoAnswerFeedback = new BoolFeedback(() => { return AutoAnswerState; }); + DoNotDisturbFeedback = new BoolFeedback(() => { return DoNotDisturbState; }); + } + + public event EventHandler CallStatusChange; + + public void Subscribe() + { + try + { + // Do subscriptions and blah blah + // This would be better using reflection JTA 2018-08-28 + PropertyInfo[] properties = Tags.GetType().GetCType().GetProperties(); + //GetPropertyValues(Tags); + + Debug.Console(2, "QscDspDialer Subscribe"); + foreach (var prop in properties) + { + //var val = prop.GetValue(obj, null); + + + Debug.Console(2, "Property {0}, {1}, {2}\n", prop.GetType().Name, prop.Name, prop.PropertyType.FullName); + if (prop.Name.Contains("Tag") && !prop.Name.Contains("keypad")) + { + var propValue = prop.GetValue(Tags, null) as string; + Debug.Console(2, "Property {0}, {1}, {2}\n", prop.GetType().Name, prop.Name, propValue); + SendSubscriptionCommand(propValue, "1"); + } + + + } + } + catch (Exception e) + { + + Debug.Console(2, "QscDspDialer Subscription Error: '{0}'\n", e); + } + + + // SendSubscriptionCommand(, "1"); + // SendSubscriptionCommand(config. , "mute", 500); + } + public void ParseSubscriptionMessage(string customName, string value) + { + + // Check for valid subscription response + Debug.Console(1, "QscDialerTag {0} Response: '{1}'", customName, value); + if (customName == Tags.dialStringTag) + { + Debug.Console(2, "QscDialerTag DialStringChanged ", value); + this.DialString = value; + this.DialStringFeedback.FireUpdate(); + } + else if (customName == Tags.doNotDisturbTag) + { + if (value == "on") + { + this.DoNotDisturbState = true; + } + else if (value == "off") + { + this.DoNotDisturbState = false; + } + DoNotDisturbFeedback.FireUpdate(); + } + else if (customName == Tags.callStatusTag) + { + if (value == "Dialing") + { + this.OffHook = true; + } + else if (value == "Disconnected") + { + this.OffHook = false; + if (Tags.ClearOnHangup) + { + this.SendKeypad(eKeypadKeys.Clear); + } + } + this.OffHookFeedback.FireUpdate(); + } + else if (customName == Tags.autoAnswerTag) + { + if (value == "on") + { + this.AutoAnswerState = true; + } + else if (value == "off") + { + this.AutoAnswerState = false; + } + AutoAnswerFeedback.FireUpdate(); + } + else if (customName == Tags.hookStatusTag) + { + if (value == "true") + { + this.OffHook = true; + } + else if (value == "false") + { + this.OffHook = false; + } + this.OffHookFeedback.FireUpdate(); + } + + + } + + public void DoNotDisturbToggle() + { + int dndStateInt = !DoNotDisturbState ? 1 : 0; + Parent.SendLine(string.Format("csv {0} {1}", Tags.doNotDisturbTag, dndStateInt)); + } + public void DoNotDisturbOn() + { + Parent.SendLine(string.Format("csv {0} 1", Tags.doNotDisturbTag)); + } + public void DoNotDisturbOff() + { + Parent.SendLine(string.Format("csv {0} 0", Tags.doNotDisturbTag)); + } + public void AutoAnswerToggle() + { + int autoAnswerStateInt = !AutoAnswerState ? 1 : 0; + Parent.SendLine(string.Format("csv {0} {1}", Tags.autoAnswerTag, autoAnswerStateInt)); + } + public void AutoAnswerOn() + { + Parent.SendLine(string.Format("csv {0} 1", Tags.autoAnswerTag)); + } + public void AutoAnswerOff() + { + Parent.SendLine(string.Format("csv {0} 0", Tags.autoAnswerTag)); + } + + private void PollKeypad() + { + Thread.Sleep(50); + Parent.SendLine(string.Format("cg {0}", Tags.dialStringTag)); + } + + public void SendKeypad(eKeypadKeys button) + { + string keypadTag = null; + switch (button) + { + case eKeypadKeys.Num0: keypadTag = Tags.keypad0Tag; break; + case eKeypadKeys.Num1: keypadTag = Tags.keypad1Tag; break; + case eKeypadKeys.Num2: keypadTag = Tags.keypad2Tag; break; + case eKeypadKeys.Num3: keypadTag = Tags.keypad3Tag; break; + case eKeypadKeys.Num4: keypadTag = Tags.keypad4Tag; break; + case eKeypadKeys.Num5: keypadTag = Tags.keypad5Tag; break; + case eKeypadKeys.Num6: keypadTag = Tags.keypad6Tag; break; + case eKeypadKeys.Num7: keypadTag = Tags.keypad7Tag; break; + case eKeypadKeys.Num8: keypadTag = Tags.keypad8Tag; break; + case eKeypadKeys.Num9: keypadTag = Tags.keypad9Tag; break; + case eKeypadKeys.Pound: keypadTag = Tags.keypadPoundTag; break; + case eKeypadKeys.Star: keypadTag = Tags.keypadStarTag; break; + case eKeypadKeys.Backspace: keypadTag = Tags.keypadBackspaceTag; break; + case eKeypadKeys.Clear: keypadTag = Tags.keypadClearTag; break; + } + if (keypadTag != null) + { + var cmdToSend = string.Format("ct {0}", keypadTag); + Parent.SendLine(cmdToSend); + PollKeypad(); + } + } + public void SendSubscriptionCommand(string instanceTag, string changeGroup) + { + // Subscription string format: InstanceTag subscribe attributeCode Index1 customName responseRate + // Ex: "RoomLevel subscribe level 1 MyRoomLevel 500" + + var cmd = string.Format("cga {0} {1}", changeGroup, instanceTag); + + Parent.SendLine(cmd); + } + public void Dial() + { + if (!this.OffHook) + { + Parent.SendLine(string.Format("ct {0}", Tags.connectTag)); + } + else + { + Parent.SendLine(string.Format("ct {0}", Tags.disconnectTag)); + } + Thread.Sleep(50); + Parent.SendLine(string.Format("cg {0}", Tags.callStatusTag)); + } + public void Dial(string number) + { + } + public void EndCall(CodecActiveCallItem activeCall) + { + } + public void EndAllCalls() + { + } + public void AcceptCall(CodecActiveCallItem item) + { + } + + public void RejectCall(CodecActiveCallItem item) + { + + } + + public void SendDtmf(string digit) + { + + } + + public enum eKeypadKeys + { + Num1, + Num2, + Num3, + Num4, + Num5, + Num6, + Num7, + Num8, + Num9, + Num0, + Star, + Pound, + Clear, + Backspace + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspLevelControl.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspLevelControl.cs new file mode 100644 index 00000000..498127f5 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspLevelControl.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Text.RegularExpressions; + + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + public class QscDspLevelControl : QscDspControlPoint, IBasicVolumeWithFeedback, IKeyed + { + bool _IsMuted; + ushort _VolumeLevel; + + public BoolFeedback MuteFeedback { get; private set; } + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public bool Enabled { get; set; } + public ePdtLevelTypes Type; + CTimer VolumeUpRepeatTimer; + CTimer VolumeDownRepeatTimer; + + /// + /// Used for to identify level subscription values + /// + public string LevelCustomName { get; private set; } + + /// + /// Used for to identify mute subscription values + /// + public string MuteCustomName { get; private set; } + + /// + /// Minimum fader level + /// + double MinLevel; + + /// + /// Maximum fader level + /// + double MaxLevel; + + /// + /// Checks if a valid subscription string has been recieved for all subscriptions + /// + public bool IsSubsribed + { + get + { + bool isSubscribed = false; + + if (HasMute && MuteIsSubscribed) + isSubscribed = true; + + if (HasLevel && LevelIsSubscribed) + isSubscribed = true; + + return isSubscribed; + } + } + + public bool AutomaticUnmuteOnVolumeUp { get; private set; } + + public bool HasMute { get; private set; } + + public bool HasLevel { get; private set; } + + bool MuteIsSubscribed; + + bool LevelIsSubscribed; + + //public TesiraForteLevelControl(string label, string id, int index1, int index2, bool hasMute, bool hasLevel, BiampTesiraForteDsp parent) + // : base(id, index1, index2, parent) + //{ + // Initialize(label, hasMute, hasLevel); + //} + + public QscDspLevelControl(string key, QscDspLevelControlBlockConfig config, QscDsp parent) + : base(config.LevelInstanceTag, config.MuteInstanceTag, parent) + { + if (!config.Disabled) + { + Initialize(key, config); + } + } + + + /// + /// Initializes this attribute based on config values and generates subscriptions commands and adds commands to the parent's queue. + /// + public void Initialize(string key, QscDspLevelControlBlockConfig config) + { + Key = string.Format("{0}--{1}", Parent.Key, key); + Enabled = true; + DeviceManager.AddDevice(this); + if (config.IsMic) + { + Type = ePdtLevelTypes.microphone; + } + else + { + Type = ePdtLevelTypes.speaker; + } + + Debug.Console(2, this, "Adding LevelControl '{0}'", Key); + + this.IsSubscribed = false; + + MuteFeedback = new BoolFeedback(() => _IsMuted); + + VolumeLevelFeedback = new IntFeedback(() => _VolumeLevel); + + VolumeUpRepeatTimer = new CTimer(VolumeUpRepeat, Timeout.Infinite); + VolumeDownRepeatTimer = new CTimer(VolumeDownRepeat, Timeout.Infinite); + LevelCustomName = config.Label; + HasMute = config.HasMute; + HasLevel = config.HasLevel; + } + + public void Subscribe() + { + // Do subscriptions and blah blah + + // Subscribe to mute + if (this.HasMute) + { + + SendSubscriptionCommand(this.MuteInstanceTag, "1"); + // SendSubscriptionCommand(config. , "mute", 500); + } + + // Subscribe to level + if (this.HasLevel) + { + + SendSubscriptionCommand(this.LevelInstanceTag, "1"); + // SendSubscriptionCommand(this.con, "level", 250); + + //SendFullCommand("get", "minLevel", null); + + //SendFullCommand("get", "maxLevel", null); + } + } + + + /// + /// Parses the response from the DspBase + /// + /// + /// + public void ParseSubscriptionMessage(string customName, string value) + { + + // Check for valid subscription response + Debug.Console(1, this, "Level {0} Response: '{1}'", customName, value); + if (customName == MuteInstanceTag) + { + if (value == "muted") + { + _IsMuted = true; + MuteIsSubscribed = true; + + } + else if (value == "unmuted") + { + _IsMuted = false; + MuteIsSubscribed = true; + } + + MuteFeedback.FireUpdate(); + } + else if (customName == LevelInstanceTag) + { + + + var _value = Double.Parse(value); + + _VolumeLevel = (ushort)(_value * 65535); + Debug.Console(1, this, "Level {0} VolumeLevel: '{1}'", customName, _VolumeLevel); + LevelIsSubscribed = true; + + VolumeLevelFeedback.FireUpdate(); + } + + } + + + /// + /// Turns the mute off + /// + public void MuteOff() + { + SendFullCommand("csv", this.MuteInstanceTag, "0"); + } + + /// + /// Turns the mute on + /// + public void MuteOn() + { + SendFullCommand("csv", this.MuteInstanceTag, "1"); + } + + /// + /// Sets the volume to a specified level + /// + /// + public void SetVolume(ushort level) + { + Debug.Console(1, this, "volume: {0}", level); + // Unmute volume if new level is higher than existing + if (AutomaticUnmuteOnVolumeUp && _IsMuted) + { + MuteOff(); + } + double newLevel = Scale(level); + Debug.Console(1, this, "newVolume: {0}", newLevel); + SendFullCommand("csp", this.LevelInstanceTag, string.Format("{0}", newLevel)); + } + + /// + /// Toggles mute status + /// + public void MuteToggle() + { + + if (_IsMuted) + { + SendFullCommand("csv", this.MuteInstanceTag, "0"); + } + else + { + SendFullCommand("csv", this.MuteInstanceTag, "1"); + } + + } + + public void VolumeUpRepeat(object callbackObject) + { + this.VolumeUp(true); + } + public void VolumeDownRepeat(object callbackObject) + { + this.VolumeDown(true); + } + + public void VolumeDown(bool press) + { + + + if (press) + { + VolumeDownRepeatTimer.Reset(100); + SendFullCommand("css ", this.LevelInstanceTag, "--"); + + + } + else + { + VolumeDownRepeatTimer.Stop(); + // VolumeDownRepeatTimer.Dispose(); + } + } + + /// + /// Increments volume level + /// + /// + public void VolumeUp(bool press) + { + if (press) + { + VolumeUpRepeatTimer.Reset(100); + SendFullCommand("css ", this.LevelInstanceTag, "++"); + + if (AutomaticUnmuteOnVolumeUp) + if (!_IsMuted) + MuteOff(); + } + else + { + VolumeUpRepeatTimer.Stop(); + } + } + /// + double Scale(double input) + { + Debug.Console(1, this, "Scaling (double) input '{0}'",input ); + + var output = (input / 65535); + + Debug.Console(1, this, "Scaled output '{0}'", output); + + return output; + } + } + public enum ePdtLevelTypes + { + speaker = 0, + microphone = 1 + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspPropertiesConfig.cs new file mode 100644 index 00000000..9eab3e84 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QSC/QscDspPropertiesConfig.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + /// + /// + /// + public class QscDspPropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public ControlPropertiesConfig Control { get; set; } + + /// + /// These are key-value pairs, string id, string type. + /// Valid types are level and mute. + /// Need to include the index values somehow + /// + public Dictionary LevelControlBlocks { get; set; } + public Dictionary dialerControlBlocks { get; set; } + public Dictionary presets { get; set; } + // public Dictionary DialerControlBlocks {get; set;} + } + public interface IQscDspBasicLevel : IBasicVolumeWithFeedback + { + /// + /// In BiAmp: Instance Tag, QSC: Named Control, Polycom: + /// + string LevelInstanceTag { get; set; } + string MuteInstanceTag { get; set; } + bool HasMute { get; } + bool HasLevel { get; } + bool AutomaticUnmuteOnVolumeUp { get; } + } + public class QscDspLevelControlBlockConfig + { + public bool Disabled { get; set; } + public string Label { get; set; } + public string LevelInstanceTag { get; set; } + public string MuteInstanceTag { get; set; } + public bool HasMute { get; set; } + public bool HasLevel { get; set; } + public bool IsMic { get; set; } + } + + public class QscDialerConfig + { + public string incomingCallRingerTag {get; set;} + public string dialStringTag {get; set;} + public string disconnectTag {get; set;} + public string connectTag {get; set;} + public string callStatusTag {get; set;} + public string hookStatusTag {get; set;} + public string doNotDisturbTag { get; set; } + public string autoAnswerTag { get; set; } + + public string keypadBackspaceTag {get; set;} + public string keypadClearTag {get; set;} + public string keypad1Tag {get; set;} + public string keypad2Tag {get; set;} + public string keypad3Tag {get; set;} + public string keypad4Tag {get; set;} + public string keypad5Tag {get; set;} + public string keypad6Tag {get; set;} + public string keypad7Tag {get; set;} + public string keypad8Tag {get; set;} + public string keypad9Tag {get; set;} + public string keypad0Tag {get; set;} + public string keypadPoundTag {get; set;} + public string keypadStarTag {get; set;} + + public bool ClearOnHangup { get; set; } + + } + + public class QscDspPresets + { + public string label { get; set; } + public string preset { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/._QscDspPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/._QscDspPropertiesConfig.cs new file mode 100644 index 00000000..8801a8a6 Binary files /dev/null and b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/._QscDspPropertiesConfig.cs differ diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDsp.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDsp.cs new file mode 100644 index 00000000..24fd93d4 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDsp.cs @@ -0,0 +1,328 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; +using System.Text.RegularExpressions; +using Crestron.SimplSharp.Reflection; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + + // QUESTIONS: + // + // When subscribing, just use the Instance ID for Custom Name? + + // Verbose on subscriptions? + + // Example subscription feedback responses + // ! "publishToken":"name" "value":-77.0 + // ! "myLevelName" -77 + + public class QscDsp : DspBase + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public GenericCommunicationMonitor CommunicationMonitor { get; private set; } + + new public Dictionary LevelControlPoints { get; private set; } + new public Dictionary Dialers { get; set; } + public List PresetList = new List(); + + public bool isSubscribed; + + + + CrestronQueue CommandQueue; + + bool CommandQueueInProgress = false; + + + public bool ShowHexResponse { get; set; } + + public QscDsp(string key, string name, IBasicCommunication comm, QscDspPropertiesConfig props) : + base(key, name) + { + CommandQueue = new CrestronQueue(100); + + Communication = comm; + var socket = comm as ISocketStatus; + if (socket != null) + { + // This instance uses IP control + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + else + { + // This instance uses RS-232 control + } + PortGather = new CommunicationGather(Communication, "\x0a"); + PortGather.LineReceived += this.Port_LineReceived; + + LevelControlPoints = new Dictionary(); + Dialers = new Dictionary(); + + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 20000, 120000, 300000, "cgp 1\x0D\x0A"); + } + + + foreach (KeyValuePair block in props.LevelControlBlocks) + { + this.LevelControlPoints.Add(block.Key, new QscDspLevelControl(block.Key, block.Value, this)); + Debug.Console(2, this, "Added LevelControlPoint {0}", block.Key); + + + } + foreach (KeyValuePair preset in props.presets) + { + this.addPreset(preset.Value); + Debug.Console(2, this, "Added Preset {0} {1}", preset.Value.label, preset.Value.preset); + } + foreach (KeyValuePair dialerConfig in props.dialerControlBlocks) + { + Debug.Console(2, this, "Added Dialer {0}\n {1}", dialerConfig.Key, dialerConfig.Value); + this.Dialers.Add(dialerConfig.Key, new QscDspDialer(dialerConfig.Value, this)); + + } + + } + + public override bool CustomActivate() + { + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + + + CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + return true; + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString()); + + if (e.Client.IsConnected) + { + SubscribeToAttributes(); + } + else + { + // Cleanup items from this session + CommandQueue.Clear(); + CommandQueueInProgress = false; + } + } + + /// + /// Initiates the subscription process to the DSP + /// + void SubscribeToAttributes() + { + SendLine("cgd 1"); + + SendLine("cgc 1"); + + foreach (KeyValuePair level in LevelControlPoints) + { + level.Value.Subscribe(); + } + + foreach (var dialer in Dialers) + { + dialer.Value.Subscribe(); + } + + if (CommunicationMonitor != null) + { + + CommunicationMonitor.Start(); + //CommunicationMonitor = null; + } + else + { + + } + //CommunicationMonitor.Message = "cgp 1\x0D\x0A"; + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + //CommunicationMonitor.Start(); + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + + // ResetSubscriptionTimer(); + } + + + + /// + /// Handles a response message from the DSP + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + Debug.Console(2, this, "RX: '{0}'", args.Text); + try + { + if (args.Text.IndexOf("sr ") > -1) + { + } + else if (args.Text.IndexOf("cv") > -1) + { + + var changeMessage = args.Text.Split(null); + + string changedInstance = changeMessage[1].Replace("\"", ""); + Debug.Console(1, this, "cv parse Instance: {0}", changedInstance); + bool foundItFlag = false; + foreach (KeyValuePair controlPoint in LevelControlPoints) + { + if (changedInstance == controlPoint.Value.LevelInstanceTag) + { + controlPoint.Value.ParseSubscriptionMessage(changedInstance, changeMessage[4]); + foundItFlag = true; + return; + } + else if (changedInstance == controlPoint.Value.MuteInstanceTag) + { + controlPoint.Value.ParseSubscriptionMessage(changedInstance, changeMessage[2].Replace("\"", "")); + foundItFlag = true; + return; + } + + } + if (!foundItFlag) + { + foreach (var dialer in Dialers) + { + PropertyInfo[] properties = dialer.Value.Tags.GetType().GetCType().GetProperties(); + //GetPropertyValues(Tags); + foreach (var prop in properties) + { + var propValue = prop.GetValue(dialer.Value.Tags, null) as string; + if (changedInstance == propValue) + { + dialer.Value.ParseSubscriptionMessage(changedInstance, changeMessage[2].Replace("\"", "")); + foundItFlag = true; + return; + } + } + if (foundItFlag) + { + return; + } + } + } + + } + + + } + catch (Exception e) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Error parsing response: '{0}'\n{1}", args.Text, e); + } + + } + + /// + /// Sends a command to the DSP (with delimiter appended) + /// + /// Command to send + public void SendLine(string s) + { + Debug.Console(1, this, "TX: '{0}'", s); + Communication.SendText(s + "\x0a"); + } + + /// + /// Adds a command from a child module to the queue + /// + /// Command object from child module + public void EnqueueCommand(QueuedCommand commandToEnqueue) + { + CommandQueue.Enqueue(commandToEnqueue); + //Debug.Console(1, this, "Command (QueuedCommand) Enqueued '{0}'. CommandQueue has '{1}' Elements.", commandToEnqueue.Command, CommandQueue.Count); + + if(!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Adds a raw string command to the queue + /// + /// + public void EnqueueCommand(string command) + { + CommandQueue.Enqueue(command); + //Debug.Console(1, this, "Command (string) Enqueued '{0}'. CommandQueue has '{1}' Elements.", command, CommandQueue.Count); + + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Sends the next queued command to the DSP + /// + void SendNextQueuedCommand() + { + if (Communication.IsConnected && !CommandQueue.IsEmpty) + { + CommandQueueInProgress = true; + + if (CommandQueue.Peek() is QueuedCommand) + { + QueuedCommand nextCommand = new QueuedCommand(); + + nextCommand = (QueuedCommand)CommandQueue.Peek(); + + SendLine(nextCommand.Command); + } + else + { + string nextCommand = (string)CommandQueue.Peek(); + + SendLine(nextCommand); + } + } + + } + + public void RunPresetNumber(ushort n) + { + RunPreset(PresetList[n].preset); + } + + public void addPreset(QscDspPresets s) + { + PresetList.Add(s); + } + /// + /// Sends a command to execute a preset + /// + /// Preset Name + public override void RunPreset(string name) + { + SendLine(string.Format("ssl {0}", name)); + SendLine("cgp 1"); + } + + public class QueuedCommand + { + public string Command { get; set; } + public string AttributeCode { get; set; } + public QscDspControlPoint ControlPoint { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspControlPoint.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspControlPoint.cs new file mode 100644 index 00000000..542336eb --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspControlPoint.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + public abstract class QscDspControlPoint : DspControlPoint + { + public string Key { get; protected set; } + + public string LevelInstanceTag { get; set; } + public string MuteInstanceTag { get; set; } + public QscDsp Parent { get; private set; } + + public bool IsSubscribed { get; protected set; } + + protected QscDspControlPoint(string levelInstanceTag, string muteInstanceTag, QscDsp parent) + { + LevelInstanceTag = levelInstanceTag; + MuteInstanceTag = muteInstanceTag; + Parent = parent; + } + + virtual public void Initialize() + { + } + + /// + /// Sends a command to the DSP + /// + /// command + /// attribute code + /// value (use "" if not applicable) + public virtual void SendFullCommand(string cmd, string instance, string value) + { + + var cmdToSemd = string.Format("{0} {1} {2}", cmd, instance, value); + + Parent.SendLine(cmdToSemd); + + } + + virtual public void ParseGetMessage(string attributeCode, string message) + { + } + + + + public virtual void SendSubscriptionCommand(string instanceTag, string changeGroup) + { + // Subscription string format: InstanceTag subscribe attributeCode Index1 customName responseRate + // Ex: "RoomLevel subscribe level 1 MyRoomLevel 500" + + string cmd; + + cmd = string.Format("cga {0} {1}", changeGroup, instanceTag); + + Parent.SendLine(cmd); + } + + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspDialer.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspDialer.cs new file mode 100644 index 00000000..387dffe7 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspDialer.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using Crestron.SimplSharp.Reflection; +using Crestron.SimplSharp; +using PepperDash.Essentials.Devices.Common.Codec; +using Crestron.SimplSharpPro.CrestronThread; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + public class QscDspDialer : DspDialerBase, IHasDialer + { + public QscDialerConfig Tags; + public bool IsInCall { get; private set; } + public QscDsp Parent { get; private set; } + public string DialString { get; private set; } + public bool OffHook { get; private set; } + public bool AutoAnswerState { get; private set; } + public bool DoNotDisturbState { get; private set; } + + public BoolFeedback OffHookFeedback; + public BoolFeedback AutoAnswerFeedback; + public BoolFeedback DoNotDisturbFeedback; + public StringFeedback DialStringFeedback; + + // Add requirements for Dialer functionality + + public QscDspDialer(QscDialerConfig Config, QscDsp parent) + { + Tags = Config; + Parent = parent; + DialStringFeedback = new StringFeedback(() => { return DialString; }); + OffHookFeedback = new BoolFeedback(() => { return OffHook; }); + AutoAnswerFeedback = new BoolFeedback(() => { return AutoAnswerState; }); + DoNotDisturbFeedback = new BoolFeedback(() => { return DoNotDisturbState; }); + } + + public event EventHandler CallStatusChange; + + public void Subscribe() + { + try + { + // Do subscriptions and blah blah + // This would be better using reflection JTA 2018-08-28 + PropertyInfo[] properties = Tags.GetType().GetCType().GetProperties(); + //GetPropertyValues(Tags); + + Debug.Console(2, "QscDspDialer Subscribe"); + foreach (var prop in properties) + { + //var val = prop.GetValue(obj, null); + + + Debug.Console(2, "Property {0}, {1}, {2}\n", prop.GetType().Name, prop.Name, prop.PropertyType.FullName); + if (prop.Name.Contains("Tag") && !prop.Name.Contains("keypad")) + { + var propValue = prop.GetValue(Tags, null) as string; + Debug.Console(2, "Property {0}, {1}, {2}\n", prop.GetType().Name, prop.Name, propValue); + SendSubscriptionCommand(propValue, "1"); + } + + + } + } + catch (Exception e) + { + + Debug.Console(2, "QscDspDialer Subscription Error: '{0}'\n", e); + } + + + // SendSubscriptionCommand(, "1"); + // SendSubscriptionCommand(config. , "mute", 500); + } + public void ParseSubscriptionMessage(string customName, string value) + { + + // Check for valid subscription response + Debug.Console(1, "QscDialerTag {0} Response: '{1}'", customName, value); + if (customName == Tags.dialStringTag) + { + Debug.Console(2, "QscDialerTag DialStringChanged ", value); + this.DialString = value; + this.DialStringFeedback.FireUpdate(); + } + else if (customName == Tags.doNotDisturbTag) + { + if (value == "on") + { + this.DoNotDisturbState = true; + } + else if (value == "off") + { + this.DoNotDisturbState = false; + } + DoNotDisturbFeedback.FireUpdate(); + } + else if (customName == Tags.callStatusTag) + { + if (value == "Dialing") + { + this.OffHook = true; + } + else if (value == "Disconnected") + { + this.OffHook = false; + if (Tags.ClearOnHangup) + { + this.SendKeypad(eKeypadKeys.Clear); + } + } + this.OffHookFeedback.FireUpdate(); + } + else if (customName == Tags.autoAnswerTag) + { + if (value == "on") + { + this.AutoAnswerState = true; + } + else if (value == "off") + { + this.AutoAnswerState = false; + } + AutoAnswerFeedback.FireUpdate(); + } + else if (customName == Tags.hookStatusTag) + { + if (value == "true") + { + this.OffHook = true; + } + else if (value == "false") + { + this.OffHook = false; + } + this.OffHookFeedback.FireUpdate(); + } + + + } + + public void DoNotDisturbToggle() + { + int dndStateInt = !DoNotDisturbState ? 1 : 0; + Parent.SendLine(string.Format("csv {0} {1}", Tags.doNotDisturbTag, dndStateInt)); + } + public void DoNotDisturbOn() + { + Parent.SendLine(string.Format("csv {0} 1", Tags.doNotDisturbTag)); + } + public void DoNotDisturbOff() + { + Parent.SendLine(string.Format("csv {0} 0", Tags.doNotDisturbTag)); + } + public void AutoAnswerToggle() + { + int autoAnswerStateInt = !AutoAnswerState ? 1 : 0; + Parent.SendLine(string.Format("csv {0} {1}", Tags.autoAnswerTag, autoAnswerStateInt)); + } + public void AutoAnswerOn() + { + Parent.SendLine(string.Format("csv {0} 1", Tags.autoAnswerTag)); + } + public void AutoAnswerOff() + { + Parent.SendLine(string.Format("csv {0} 0", Tags.autoAnswerTag)); + } + + private void PollKeypad() + { + Thread.Sleep(50); + Parent.SendLine(string.Format("cg {0}", Tags.dialStringTag)); + } + + public void SendKeypad(eKeypadKeys button) + { + string keypadTag = null; + switch (button) + { + case eKeypadKeys.Num0: keypadTag = Tags.keypad0Tag; break; + case eKeypadKeys.Num1: keypadTag = Tags.keypad1Tag; break; + case eKeypadKeys.Num2: keypadTag = Tags.keypad2Tag; break; + case eKeypadKeys.Num3: keypadTag = Tags.keypad3Tag; break; + case eKeypadKeys.Num4: keypadTag = Tags.keypad4Tag; break; + case eKeypadKeys.Num5: keypadTag = Tags.keypad5Tag; break; + case eKeypadKeys.Num6: keypadTag = Tags.keypad6Tag; break; + case eKeypadKeys.Num7: keypadTag = Tags.keypad7Tag; break; + case eKeypadKeys.Num8: keypadTag = Tags.keypad8Tag; break; + case eKeypadKeys.Num9: keypadTag = Tags.keypad9Tag; break; + case eKeypadKeys.Pound: keypadTag = Tags.keypadPoundTag; break; + case eKeypadKeys.Star: keypadTag = Tags.keypadStarTag; break; + case eKeypadKeys.Backspace: keypadTag = Tags.keypadBackspaceTag; break; + case eKeypadKeys.Clear: keypadTag = Tags.keypadClearTag; break; + } + if (keypadTag != null) + { + var cmdToSend = string.Format("ct {0}", keypadTag); + Parent.SendLine(cmdToSend); + PollKeypad(); + } + } + public void SendSubscriptionCommand(string instanceTag, string changeGroup) + { + // Subscription string format: InstanceTag subscribe attributeCode Index1 customName responseRate + // Ex: "RoomLevel subscribe level 1 MyRoomLevel 500" + + var cmd = string.Format("cga {0} {1}", changeGroup, instanceTag); + + Parent.SendLine(cmd); + } + public void Dial() + { + if (!this.OffHook) + { + Parent.SendLine(string.Format("ct {0}", Tags.connectTag)); + } + else + { + Parent.SendLine(string.Format("ct {0}", Tags.disconnectTag)); + } + Thread.Sleep(50); + Parent.SendLine(string.Format("cg {0}", Tags.callStatusTag)); + } + public void Dial(string number) + { + } + public void EndCall(CodecActiveCallItem activeCall) + { + } + public void EndAllCalls() + { + } + public void AcceptCall(CodecActiveCallItem item) + { + } + + public void RejectCall(CodecActiveCallItem item) + { + + } + + public void SendDtmf(string digit) + { + + } + + public enum eKeypadKeys + { + Num1, + Num2, + Num3, + Num4, + Num5, + Num6, + Num7, + Num8, + Num9, + Num0, + Star, + Pound, + Clear, + Backspace + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspLevelControl.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspLevelControl.cs new file mode 100644 index 00000000..498127f5 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspLevelControl.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Text.RegularExpressions; + + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + public class QscDspLevelControl : QscDspControlPoint, IBasicVolumeWithFeedback, IKeyed + { + bool _IsMuted; + ushort _VolumeLevel; + + public BoolFeedback MuteFeedback { get; private set; } + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public bool Enabled { get; set; } + public ePdtLevelTypes Type; + CTimer VolumeUpRepeatTimer; + CTimer VolumeDownRepeatTimer; + + /// + /// Used for to identify level subscription values + /// + public string LevelCustomName { get; private set; } + + /// + /// Used for to identify mute subscription values + /// + public string MuteCustomName { get; private set; } + + /// + /// Minimum fader level + /// + double MinLevel; + + /// + /// Maximum fader level + /// + double MaxLevel; + + /// + /// Checks if a valid subscription string has been recieved for all subscriptions + /// + public bool IsSubsribed + { + get + { + bool isSubscribed = false; + + if (HasMute && MuteIsSubscribed) + isSubscribed = true; + + if (HasLevel && LevelIsSubscribed) + isSubscribed = true; + + return isSubscribed; + } + } + + public bool AutomaticUnmuteOnVolumeUp { get; private set; } + + public bool HasMute { get; private set; } + + public bool HasLevel { get; private set; } + + bool MuteIsSubscribed; + + bool LevelIsSubscribed; + + //public TesiraForteLevelControl(string label, string id, int index1, int index2, bool hasMute, bool hasLevel, BiampTesiraForteDsp parent) + // : base(id, index1, index2, parent) + //{ + // Initialize(label, hasMute, hasLevel); + //} + + public QscDspLevelControl(string key, QscDspLevelControlBlockConfig config, QscDsp parent) + : base(config.LevelInstanceTag, config.MuteInstanceTag, parent) + { + if (!config.Disabled) + { + Initialize(key, config); + } + } + + + /// + /// Initializes this attribute based on config values and generates subscriptions commands and adds commands to the parent's queue. + /// + public void Initialize(string key, QscDspLevelControlBlockConfig config) + { + Key = string.Format("{0}--{1}", Parent.Key, key); + Enabled = true; + DeviceManager.AddDevice(this); + if (config.IsMic) + { + Type = ePdtLevelTypes.microphone; + } + else + { + Type = ePdtLevelTypes.speaker; + } + + Debug.Console(2, this, "Adding LevelControl '{0}'", Key); + + this.IsSubscribed = false; + + MuteFeedback = new BoolFeedback(() => _IsMuted); + + VolumeLevelFeedback = new IntFeedback(() => _VolumeLevel); + + VolumeUpRepeatTimer = new CTimer(VolumeUpRepeat, Timeout.Infinite); + VolumeDownRepeatTimer = new CTimer(VolumeDownRepeat, Timeout.Infinite); + LevelCustomName = config.Label; + HasMute = config.HasMute; + HasLevel = config.HasLevel; + } + + public void Subscribe() + { + // Do subscriptions and blah blah + + // Subscribe to mute + if (this.HasMute) + { + + SendSubscriptionCommand(this.MuteInstanceTag, "1"); + // SendSubscriptionCommand(config. , "mute", 500); + } + + // Subscribe to level + if (this.HasLevel) + { + + SendSubscriptionCommand(this.LevelInstanceTag, "1"); + // SendSubscriptionCommand(this.con, "level", 250); + + //SendFullCommand("get", "minLevel", null); + + //SendFullCommand("get", "maxLevel", null); + } + } + + + /// + /// Parses the response from the DspBase + /// + /// + /// + public void ParseSubscriptionMessage(string customName, string value) + { + + // Check for valid subscription response + Debug.Console(1, this, "Level {0} Response: '{1}'", customName, value); + if (customName == MuteInstanceTag) + { + if (value == "muted") + { + _IsMuted = true; + MuteIsSubscribed = true; + + } + else if (value == "unmuted") + { + _IsMuted = false; + MuteIsSubscribed = true; + } + + MuteFeedback.FireUpdate(); + } + else if (customName == LevelInstanceTag) + { + + + var _value = Double.Parse(value); + + _VolumeLevel = (ushort)(_value * 65535); + Debug.Console(1, this, "Level {0} VolumeLevel: '{1}'", customName, _VolumeLevel); + LevelIsSubscribed = true; + + VolumeLevelFeedback.FireUpdate(); + } + + } + + + /// + /// Turns the mute off + /// + public void MuteOff() + { + SendFullCommand("csv", this.MuteInstanceTag, "0"); + } + + /// + /// Turns the mute on + /// + public void MuteOn() + { + SendFullCommand("csv", this.MuteInstanceTag, "1"); + } + + /// + /// Sets the volume to a specified level + /// + /// + public void SetVolume(ushort level) + { + Debug.Console(1, this, "volume: {0}", level); + // Unmute volume if new level is higher than existing + if (AutomaticUnmuteOnVolumeUp && _IsMuted) + { + MuteOff(); + } + double newLevel = Scale(level); + Debug.Console(1, this, "newVolume: {0}", newLevel); + SendFullCommand("csp", this.LevelInstanceTag, string.Format("{0}", newLevel)); + } + + /// + /// Toggles mute status + /// + public void MuteToggle() + { + + if (_IsMuted) + { + SendFullCommand("csv", this.MuteInstanceTag, "0"); + } + else + { + SendFullCommand("csv", this.MuteInstanceTag, "1"); + } + + } + + public void VolumeUpRepeat(object callbackObject) + { + this.VolumeUp(true); + } + public void VolumeDownRepeat(object callbackObject) + { + this.VolumeDown(true); + } + + public void VolumeDown(bool press) + { + + + if (press) + { + VolumeDownRepeatTimer.Reset(100); + SendFullCommand("css ", this.LevelInstanceTag, "--"); + + + } + else + { + VolumeDownRepeatTimer.Stop(); + // VolumeDownRepeatTimer.Dispose(); + } + } + + /// + /// Increments volume level + /// + /// + public void VolumeUp(bool press) + { + if (press) + { + VolumeUpRepeatTimer.Reset(100); + SendFullCommand("css ", this.LevelInstanceTag, "++"); + + if (AutomaticUnmuteOnVolumeUp) + if (!_IsMuted) + MuteOff(); + } + else + { + VolumeUpRepeatTimer.Stop(); + } + } + /// + double Scale(double input) + { + Debug.Console(1, this, "Scaling (double) input '{0}'",input ); + + var output = (input / 65535); + + Debug.Console(1, this, "Scaled output '{0}'", output); + + return output; + } + } + public enum ePdtLevelTypes + { + speaker = 0, + microphone = 1 + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspPropertiesConfig.cs new file mode 100644 index 00000000..9eab3e84 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/QscDsp/QscDspPropertiesConfig.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.DSP +{ + /// + /// + /// + public class QscDspPropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public ControlPropertiesConfig Control { get; set; } + + /// + /// These are key-value pairs, string id, string type. + /// Valid types are level and mute. + /// Need to include the index values somehow + /// + public Dictionary LevelControlBlocks { get; set; } + public Dictionary dialerControlBlocks { get; set; } + public Dictionary presets { get; set; } + // public Dictionary DialerControlBlocks {get; set;} + } + public interface IQscDspBasicLevel : IBasicVolumeWithFeedback + { + /// + /// In BiAmp: Instance Tag, QSC: Named Control, Polycom: + /// + string LevelInstanceTag { get; set; } + string MuteInstanceTag { get; set; } + bool HasMute { get; } + bool HasLevel { get; } + bool AutomaticUnmuteOnVolumeUp { get; } + } + public class QscDspLevelControlBlockConfig + { + public bool Disabled { get; set; } + public string Label { get; set; } + public string LevelInstanceTag { get; set; } + public string MuteInstanceTag { get; set; } + public bool HasMute { get; set; } + public bool HasLevel { get; set; } + public bool IsMic { get; set; } + } + + public class QscDialerConfig + { + public string incomingCallRingerTag {get; set;} + public string dialStringTag {get; set;} + public string disconnectTag {get; set;} + public string connectTag {get; set;} + public string callStatusTag {get; set;} + public string hookStatusTag {get; set;} + public string doNotDisturbTag { get; set; } + public string autoAnswerTag { get; set; } + + public string keypadBackspaceTag {get; set;} + public string keypadClearTag {get; set;} + public string keypad1Tag {get; set;} + public string keypad2Tag {get; set;} + public string keypad3Tag {get; set;} + public string keypad4Tag {get; set;} + public string keypad5Tag {get; set;} + public string keypad6Tag {get; set;} + public string keypad7Tag {get; set;} + public string keypad8Tag {get; set;} + public string keypad9Tag {get; set;} + public string keypad0Tag {get; set;} + public string keypadPoundTag {get; set;} + public string keypadStarTag {get; set;} + + public bool ClearOnHangup { get; set; } + + } + + public class QscDspPresets + { + public string label { get; set; } + public string preset { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DiscPlayer/IRDiscPlayerBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DiscPlayer/IRDiscPlayerBase.cs new file mode 100644 index 00000000..50190294 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DiscPlayer/IRDiscPlayerBase.cs @@ -0,0 +1,314 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Common +{ + public class IRBlurayBase : Device, IDiscPlayerControls, IUiDisplayInfo, IRoutingOutputs, IUsageTracking + { + public IrOutputPortController IrPort { get; private set; } + + public uint DisplayUiType { get { return DisplayUiConstants.TypeBluray; } } + + public IRBlurayBase(string key, string name, IrOutputPortController portCont) + : base(key, name) + { + IrPort = portCont; + DeviceManager.AddDevice(portCont); + + HasKeypadAccessoryButton1 = true; + KeypadAccessoryButton1Command = "Clear"; + KeypadAccessoryButton1Label = "Clear"; + + HasKeypadAccessoryButton2 = true; + KeypadAccessoryButton2Command = "NumericEnter"; + KeypadAccessoryButton2Label = "Enter"; + + PowerIsOnFeedback = new BoolFeedback(() => _PowerIsOn); + + HdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + AnyAudioOut = new RoutingOutputPort(RoutingPortNames.AnyAudioOut, eRoutingSignalType.Audio, + eRoutingPortConnectionType.DigitalAudio, null, this); + OutputPorts = new RoutingPortCollection { HdmiOut, AnyAudioOut }; + } + + + #region IDPad Members + + public void Up(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_UP_ARROW, pressRelease); + } + + public void Down(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_DN_ARROW, pressRelease); + } + + public void Left(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_LEFT_ARROW, pressRelease); + } + + public void Right(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_RIGHT_ARROW, pressRelease); + } + + public void Select(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_ENTER, pressRelease); + } + + public void Menu(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_MENU, pressRelease); + } + + public void Exit(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_EXIT, pressRelease); + } + + #endregion + + #region INumericKeypad Members + + public void Digit0(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_0, pressRelease); + } + + public void Digit1(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_1, pressRelease); + } + + public void Digit2(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_2, pressRelease); + } + + public void Digit3(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_3, pressRelease); + } + + public void Digit4(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_4, pressRelease); + } + + public void Digit5(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_5, pressRelease); + } + + public void Digit6(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_6, pressRelease); + } + + public void Digit7(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_7, pressRelease); + } + + public void Digit8(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_8, pressRelease); + } + + public void Digit9(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_9, pressRelease); + } + + /// + /// Defaults to true + /// + public bool HasKeypadAccessoryButton1 { get; set; } + + /// + /// Defaults to "-" + /// + public string KeypadAccessoryButton1Label { get; set; } + + + /// + /// Defaults to "Dash" + /// + public string KeypadAccessoryButton1Command { get; set; } + + public void KeypadAccessoryButton1(bool pressRelease) + { + IrPort.PressRelease(KeypadAccessoryButton1Command, pressRelease); + } + + /// + /// Defaults to true + /// + public bool HasKeypadAccessoryButton2 { get; set; } + + /// + /// Defaults to "Enter" + /// + public string KeypadAccessoryButton2Label { get; set; } + + + /// + /// Defaults to "Enter" + /// + public string KeypadAccessoryButton2Command { get; set; } + + public void KeypadAccessoryButton2(bool pressRelease) + { + IrPort.PressRelease(KeypadAccessoryButton2Command, pressRelease); + } + + #endregion + + #region IChannelFunctions Members + + public void ChannelUp(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_CH_PLUS, pressRelease); + } + + public void ChannelDown(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_CH_MINUS, pressRelease); + } + + public void LastChannel(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_LAST, pressRelease); + } + + public void Guide(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_GUIDE, pressRelease); + } + + public void Info(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_INFO, pressRelease); + } + + #endregion + + #region IColorFunctions Members + + public void Red(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_RED, pressRelease); + } + + public void Green(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_GREEN, pressRelease); + } + + public void Yellow(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_YELLOW, pressRelease); + } + + public void Blue(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_BLUE, pressRelease); + } + + #endregion + + #region IRoutingOutputs Members + + public RoutingOutputPort HdmiOut { get; private set; } + public RoutingOutputPort AnyAudioOut { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + #region IPower Members + + public void PowerOn() + { + IrPort.Pulse(IROutputStandardCommands.IROut_POWER_ON, 200); + _PowerIsOn = true; + } + + public void PowerOff() + { + IrPort.Pulse(IROutputStandardCommands.IROut_POWER_OFF, 200); + _PowerIsOn = false; + } + + public void PowerToggle() + { + IrPort.Pulse(IROutputStandardCommands.IROut_POWER, 200); + _PowerIsOn = false; + } + + public BoolFeedback PowerIsOnFeedback { get; set; } + bool _PowerIsOn; + + #endregion + + #region ITransport Members + + public void Play(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_PLAY, pressRelease); + } + + public void Pause(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_PAUSE, pressRelease); + } + + public void Rewind(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_RSCAN, pressRelease); + } + + public void FFwd(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_FSCAN, pressRelease); + } + + public void ChapMinus(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_TRACK_MINUS, pressRelease); + } + + public void ChapPlus(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_TRACK_PLUS, pressRelease); + } + + public void Stop(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_STOP, pressRelease); + } + + public void Record(bool pressRelease) + { + } + + #endregion + + #region IUsageTracking Members + + public UsageTracking UsageTracker { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/AvocorVTFDisplay.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/AvocorVTFDisplay.cs new file mode 100644 index 00000000..7b11ed33 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/AvocorVTFDisplay.cs @@ -0,0 +1,720 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.CrestronThread; +using Crestron.SimplSharpPro; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Displays +{ + /// + /// + /// + public class AvocorDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, ICommunicationMonitor, IInputDisplayPort1, + IInputHdmi1, IInputHdmi2, IInputHdmi3, IInputHdmi4, IInputVga1 + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + public byte ID { get; private set; } + + /// + /// 0x08 + /// + const byte InputVga1Value = 0x00; + /// + /// 0x09 + /// + const byte InputHdmi1Value = 0x09; + /// + /// 0x0a + /// + const byte InputHdmi2Value = 0x0a; + /// + /// 0x0b + /// + const byte InputHdmi3Value = 0x0b; + /// + /// 0x0c + /// + const byte InputHdmi4Value = 0x0c; + /// + /// 0c0d + /// + const byte InputDisplayPort1Value = 0x0d; + /// + /// 0x0e + /// + const byte InputIpcOpsValue = 0x0e; + /// + /// 0x11 + /// + const byte InputHdmi5Value = 0x11; + /// + /// 0x12 + /// + const byte InputMediaPlayerValue = 0x12; + + + bool _PowerIsOn; + bool _IsWarmingUp; + bool _IsCoolingDown; + ushort _VolumeLevelForSig; + int _LastVolumeSent; + ushort _PreMuteVolumeLevel; + bool _IsMuted; + RoutingInputPort _CurrentInputPort; + ActionIncrementer VolumeIncrementer; + bool VolumeIsRamping; + public bool IsInStandby { get; private set; } + + protected override Func PowerIsOnFeedbackFunc { get { return () => _PowerIsOn; } } + protected override Func IsCoolingDownFeedbackFunc { get { return () => _IsCoolingDown; } } + protected override Func IsWarmingUpFeedbackFunc { get { return () => _IsWarmingUp; } } + protected override Func CurrentInputFeedbackFunc { get { return () => _CurrentInputPort.Key; } } + + /// + /// Constructor for IBasicCommunication + /// + public AvocorDisplay(string key, string name, IBasicCommunication comm, string id) + : base(key, name) + { + Communication = comm; + //Communication.BytesReceived += new EventHandler(Communication_BytesReceived); + + PortGather = new CommunicationGather(Communication, '\x08'); + PortGather.IncludeDelimiter = true; + PortGather.LineReceived += new EventHandler(PortGather_LineReceived); + + ID = id == null ? (byte)0x01 : Convert.ToByte(id, 16); // If id is null, set default value of 0x01, otherwise assign value passed in constructor + Init(); + } + + /// + /// Constructor for TCP + /// + public AvocorDisplay(string key, string name, string hostname, int port, string id) + : base(key, name) + { + Communication = new GenericTcpIpClient(key + "-tcp", hostname, port, 5000); + + PortGather = new CommunicationGather(Communication, '\x0d'); + PortGather.IncludeDelimiter = true; + PortGather.LineReceived += new EventHandler(PortGather_LineReceived); + + ID = id == null ? (byte)0x01 : Convert.ToByte(id, 16); // If id is null, set default value of 0x01, otherwise assign value passed in constructor + Init(); + } + + /// + /// Constructor for COM + /// + public AvocorDisplay(string key, string name, ComPort port, ComPort.ComPortSpec spec, string id) + : base(key, name) + { + Communication = new ComPortController(key + "-com", port, spec); + + PortGather = new CommunicationGather(Communication, '\x0d'); + PortGather.IncludeDelimiter = true; + PortGather.LineReceived += new EventHandler(PortGather_LineReceived); + + ID = id == null ? (byte)0x01 : Convert.ToByte(id, 16); // If id is null, set default value of 0x01, otherwise assign value passed in constructor + Init(); + } + + void AddRoutingInputPort(RoutingInputPort port, byte fbMatch) + { + port.FeedbackMatchObject = fbMatch; + InputPorts.Add(port); + } + + void Init() + { + WarmupTime = 10000; + CooldownTime = 8000; + + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, StatusGet); + DeviceManager.AddDevice(CommunicationMonitor); + + VolumeIncrementer = new ActionIncrementer(655, 0, 65535, 800, 80, + v => SetVolume((ushort)v), + () => _LastVolumeSent); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi1), this), InputHdmi1Value); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi2), this), InputHdmi2Value); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi3), this), InputHdmi3Value); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi4), this), InputHdmi4Value); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn5, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi5), this), InputHdmi5Value); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.DisplayPortIn1, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DisplayPort, new Action(InputDisplayPort1), this), InputDisplayPort1Value); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.VgaIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Dvi, new Action(InputVga1), this), InputVga1Value); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.IpcOps, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Composite, new Action(InputIpcOps), this), InputIpcOpsValue); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.MediaPlayer, eRoutingSignalType.Video, + eRoutingPortConnectionType.Vga, new Action(InputMediaPlayer), this), InputMediaPlayerValue); + + VolumeLevelFeedback = new IntFeedback(() => { return _VolumeLevelForSig; }); + MuteFeedback = new BoolFeedback(() => _IsMuted); + } + + /// + /// + /// + /// + public override bool CustomActivate() + { + Communication.Connect(); + + var socket = Communication as ISocketStatus; + if (socket != null) + { + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + + StatusGet(); + + return true; + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + if (e.Client.IsConnected) + StatusGet(); + } + + public override FeedbackCollection Feedbacks + { + get + { + var list = base.Feedbacks; + list.AddRange(new List + { + VolumeLevelFeedback, + MuteFeedback, + CurrentInputFeedback + }); + return list; + } + } + + ///// + ///// / + ///// + ///// + //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++) + // { + // if (newBytes[i] == 0xAA && newBytes[i + 1] == 0xFF) + // { + // 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) + // { + // 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) + // { + // switch (message[1]) // type byte + // { + // case 0x00: // General status + // UpdatePowerFB(message[2], message[5]); // "power" can be misrepresented when the display sleeps + // UpdateInputFb(message[5]); + // UpdateVolumeFB(message[3]); + // UpdateMuteFb(message[4]); + // UpdateInputFb(message[5]); + // break; + + // case 0x11: + // UpdatePowerFB(message[2]); + // break; + + // case 0x12: + // UpdateVolumeFB(message[2]); + // break; + + // case 0x13: + // UpdateMuteFb(message[2]); + // break; + + // case 0x14: + // UpdateInputFb(message[2]); + // break; + + // default: + // break; + // } + // } + // // 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; + //} + + void PortGather_LineReceived(object sender, GenericCommMethodReceiveTextArgs e) + { + Debug.Console(1, this, "Receivied: '{0}'", ComTextHelper.GetEscapedText(e.Text)); + + if (e.Text.IndexOf("\x50\x4F\x57") > -1) + { + // Power Status Response + + var value = e.Text.ToCharArray(); + + switch (value[6]) + { + case '\x00': + { + _PowerIsOn = false; + break; + } + case '\x01': + { + _PowerIsOn = true; + break; + } + } + + PowerIsOnFeedback.FireUpdate(); + Debug.Console(1, this, "PowerIsOn State: {0}", PowerIsOnFeedback.BoolValue); + + } + else if (e.Text.IndexOf("\x4D\x49\x4E") > -1) + { + var value = e.Text.ToCharArray(); + + var b = value[6]; + + var newInput = InputPorts.FirstOrDefault(i => i.FeedbackMatchObject.Equals(b)); + if (newInput != null && newInput != _CurrentInputPort) + { + _CurrentInputPort = newInput; + CurrentInputFeedback.FireUpdate(); + Debug.Console(1, this, "Current Input: {0}", CurrentInputFeedback.StringValue); + } + + } + else if (e.Text.IndexOf("\x56\x4F\x4C") > -1) + { + // Volume Status Response + + var value = e.Text.ToCharArray(); + + var b = value[6]; + + var newVol = (ushort)NumericalHelpers.Scale((double)b, 0, 100, 0, 65535); + if (!VolumeIsRamping) + _LastVolumeSent = newVol; + if (newVol != _VolumeLevelForSig) + { + _VolumeLevelForSig = newVol; + VolumeLevelFeedback.FireUpdate(); + + if (_VolumeLevelForSig > 0) + _IsMuted = false; + else + _IsMuted = true; + + MuteFeedback.FireUpdate(); + + Debug.Console(1, this, "Volume Level: {0}", VolumeLevelFeedback.IntValue); + } + } + + } + + /// + /// + /// + void UpdatePowerFB(byte powerByte) + { + var newVal = powerByte == 1; + if (newVal != _PowerIsOn) + { + _PowerIsOn = newVal; + PowerIsOnFeedback.FireUpdate(); + } + } + + /// + /// Updates power status from general updates where source is included. + /// Compensates for errant standby / power off hiccups by ignoring + /// power off states with input < 0x10 + /// + void UpdatePowerFB(byte powerByte, byte inputByte) + { + // This should reject errant power feedbacks when switching away from input on standby. + if (powerByte == 0x01 && inputByte < 0x10) + IsInStandby = true; + if (powerByte == 0x00 && IsInStandby) // Ignore power off if coming from standby - glitch + { + IsInStandby = false; + return; + } + + UpdatePowerFB(powerByte); + } + + /// + /// + /// + void UpdateVolumeFB(byte b) + { + var newVol = (ushort)NumericalHelpers.Scale((double)b, 0, 100, 0, 65535); + if (!VolumeIsRamping) + _LastVolumeSent = newVol; + if (newVol != _VolumeLevelForSig) + { + _VolumeLevelForSig = newVol; + VolumeLevelFeedback.FireUpdate(); + } + } + + /// + /// + /// + void UpdateMuteFb(byte b) + { + var newMute = b == 1; + if (newMute != _IsMuted) + { + _IsMuted = newMute; + MuteFeedback.FireUpdate(); + } + } + + /// + /// + /// + void UpdateInputFb(byte b) + { + var newInput = InputPorts.FirstOrDefault(i => i.FeedbackMatchObject.Equals(b)); + if (newInput != null && newInput != _CurrentInputPort) + { + _CurrentInputPort = newInput; + CurrentInputFeedback.FireUpdate(); + } + } + + /// + /// Formats an outgoing message. Replaces third byte with ID and replaces last byte with checksum + /// + /// + void SendBytes(byte[] b) + { + b[1] = ID; + + var command = b.ToString(); + + Debug.Console(1, this, "Sending: '{0}'",ComTextHelper.GetEscapedText(b)); + + Communication.SendBytes(b); + } + + + /// + /// + /// + public void StatusGet() + { + PowerGet(); + + CrestronEnvironment.Sleep(100); + + InputGet(); + + CrestronEnvironment.Sleep(100); + + VolumeGet(); + } + + /// + /// + /// + public override void PowerOn() + { + //Send(PowerOnCmd); + SendBytes(new byte[] { 0x07, ID, 0x02, 0x50, 0x4F, 0x57, 0x01, 0x08, 0x0d }); + if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown) + { + _IsWarmingUp = true; + IsWarmingUpFeedback.FireUpdate(); + // Fake power-up cycle + WarmupTimer = new CTimer(o => + { + _IsWarmingUp = false; + _PowerIsOn = true; + IsWarmingUpFeedback.FireUpdate(); + PowerIsOnFeedback.FireUpdate(); + }, WarmupTime); + } + } + + /// + /// + /// + public override void PowerOff() + { + // If a display has unreliable-power off feedback, just override this and + // remove this check. + if (!_IsWarmingUp && !_IsCoolingDown) // PowerIsOnFeedback.BoolValue && + { + //Send(PowerOffCmd); + SendBytes(new byte[] { 0x07, ID, 0x02, 0x50, 0x4F, 0x57, 0x00, 0x08, 0x0d }); + _IsCoolingDown = true; + _PowerIsOn = false; + PowerIsOnFeedback.FireUpdate(); + IsCoolingDownFeedback.FireUpdate(); + // Fake cool-down cycle + CooldownTimer = new CTimer(o => + { + _IsCoolingDown = false; + IsCoolingDownFeedback.FireUpdate(); + }, CooldownTime); + } + } + + public override void PowerToggle() + { + if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue) + PowerOff(); + else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue) + PowerOn(); + } + + public void PowerGet() + { + SendBytes(new byte[] { 0x07, ID, 0x01, 0x50, 0x4F, 0x57, 0x08, 0x0d }); + } + + public void InputHdmi1() + { + SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputHdmi1Value, 0x08, 0x0d }); + } + + public void InputHdmi2() + { + SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputHdmi2Value, 0x08, 0x0d }); + } + + public void InputHdmi3() + { + SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputHdmi3Value, 0x08, 0x0d }); + } + + public void InputHdmi4() + { + SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputHdmi4Value, 0x08, 0x0d }); + } + + public void InputHdmi5() + { + SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputHdmi5Value, 0x08, 0x0d }); + } + + public void InputDisplayPort1() + { + SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputDisplayPort1Value, 0x08, 0x0d }); + } + + public void InputVga1() + { + SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputVga1Value, 0x08, 0x0d }); + } + + public void InputIpcOps() + { + SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputIpcOpsValue, 0x08, 0x0d }); + } + + public void InputMediaPlayer() + { + SendBytes(new byte[] { 0x07, ID, 0x02, 0x4D, 0x49, 0x4E, InputMediaPlayerValue, 0x08, 0x0d }); + } + + public void InputGet() + { + SendBytes(new byte[] { 0x07, ID, 0x01, 0x4D, 0x49, 0x4E, 0x08, 0x0d }); + } + + public void VolumeGet() + { + SendBytes(new byte[] { 0x07, ID, 0x01, 0x56, 0x4F, 0x4C, 0x08, 0x0d }); + } + + + /// + /// Executes a switch, turning on display if necessary. + /// + /// + public override void ExecuteSwitch(object selector) + { + //if (!(selector is Action)) + // Debug.Console(1, this, "WARNING: ExecuteSwitch cannot handle type {0}", selector.GetType()); + + if (_PowerIsOn) + (selector as Action)(); + else // if power is off, wait until we get on FB to send it. + { + // One-time event handler to wait for power on before executing switch + EventHandler handler = null; // necessary to allow reference inside lambda to handler + handler = (o, a) => + { + if (!_IsWarmingUp) // Done warming + { + IsWarmingUpFeedback.OutputChange -= handler; + (selector as Action)(); + } + }; + IsWarmingUpFeedback.OutputChange += handler; // attach and wait for on FB + PowerOn(); + } + } + + /// + /// Scales the level to the range of the display and sends the command + /// + /// + public void SetVolume(ushort level) + { + _LastVolumeSent = level; + var scaled = (int)NumericalHelpers.Scale(level, 0, 65535, 0, 100); + // The inputs to Scale ensure that byte won't overflow + SendBytes(new byte[] { 0x07, ID, 0x02, 0x56, 0x4F, 0x4C, Convert.ToByte(scaled), 0x08, 0x0d }); + } + + #region IBasicVolumeWithFeedback Members + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public BoolFeedback MuteFeedback { get; private set; } + + /// + /// + /// + public void MuteOff() + { + SetVolume(_PreMuteVolumeLevel); + } + + /// + /// + /// + public void MuteOn() + { + _PreMuteVolumeLevel = _VolumeLevelForSig; + + SetVolume(0); + } + + ///// + ///// + ///// + //public void MuteGet() + //{ + // SendBytes(new byte[] { 0x07, ID, 0x01, }); + //} + + #endregion + + #region IBasicVolumeControls Members + + /// + /// + /// + public void MuteToggle() + { + if (_IsMuted) + MuteOff(); + else + MuteOn(); + } + + /// + /// + /// + /// + public void VolumeDown(bool pressRelease) + { + if (pressRelease) + { + VolumeIncrementer.StartDown(); + VolumeIsRamping = true; + } + else + { + VolumeIsRamping = false; + VolumeIncrementer.Stop(); + } + } + + /// + /// + /// + /// + public void VolumeUp(bool pressRelease) + { + if (pressRelease) + { + VolumeIncrementer.StartUp(); + VolumeIsRamping = true; + } + else + { + VolumeIsRamping = false; + VolumeIncrementer.Stop(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/ComTcpDisplayBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/ComTcpDisplayBase.cs new file mode 100644 index 00000000..da92df1f --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/ComTcpDisplayBase.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using PepperDash.Core; +using PepperDash.Essentials.Core; + + +namespace PepperDash.Essentials.Devices.Displays +{ + public abstract class ComTcpDisplayBase : DisplayBase, IPower + { + /// + /// Sets the communication method for this - swaps out event handlers and output handlers + /// + public IBasicCommunication CommunicationMethod + { + get { return _CommunicationMethod; } + set + { + if (_CommunicationMethod != null) + _CommunicationMethod.BytesReceived -= this.CommunicationMethod_BytesReceived; + // Outputs??? + _CommunicationMethod = value; + if (_CommunicationMethod != null) + _CommunicationMethod.BytesReceived += this.CommunicationMethod_BytesReceived; + // Outputs? + } + } + IBasicCommunication _CommunicationMethod; + + public ComTcpDisplayBase(string key, string name) + : base(key, name) + { + + } + + + protected abstract void CommunicationMethod_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs args); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/DeviceFactory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/DeviceFactory.cs new file mode 100644 index 00000000..8b4941d7 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/DeviceFactory.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharpPro; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.Devices.Displays +{ + public class DisplayDeviceFactory + { + public static IKeyed GetDevice(DeviceConfig dc) + { + var key = dc.Key; + var name = dc.Name; + var type = dc.Type; + var properties = dc.Properties; + + var typeName = dc.Type.ToLower(); + + try + { + if (typeName == "necmpsx") + { + var comm = CommFactory.CreateCommForDevice(dc); + if (comm != null) + return new NecPSXMDisplay(dc.Key, dc.Name, comm); + } + if (typeName == "panasonicthef") + { + var comm = CommFactory.CreateCommForDevice(dc); + if (comm != null) + return new PanasonicThefDisplay(dc.Key, dc.Name, comm); + } + else if(typeName == "samsungmdc") + { + var comm = CommFactory.CreateCommForDevice(dc); + if (comm != null) + return new SamsungMDC(dc.Key, dc.Name, comm, dc.Properties["id"].Value()); + } + if (typeName == "avocorvtf") + { + var comm = CommFactory.CreateCommForDevice(dc); + if (comm != null) + return new AvocorDisplay(dc.Key, dc.Name, comm, null); + } + + } + catch (Exception e) + { + Debug.Console(0, "Displays factory: Exception creating device type {0}, key {1}: \nCONFIG JSON: {2} \nERROR: {3}\n\n", + dc.Type, dc.Key, JsonConvert.SerializeObject(dc), e); + return null; + } + + return null; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/InputInterfaces.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/InputInterfaces.cs new file mode 100644 index 00000000..ce7113e1 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/InputInterfaces.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Displays +{ + public interface IInputHdmi1 { void InputHdmi1(); } + public interface IInputHdmi2 { void InputHdmi2(); } + public interface IInputHdmi3 { void InputHdmi3(); } + public interface IInputHdmi4 { void InputHdmi4(); } + public interface IInputDisplayPort1 { void InputDisplayPort1(); } + public interface IInputDisplayPort2 { void InputDisplayPort2(); } + public interface IInputVga1 { void InputVga1(); } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/NECPSXMDisplay.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/NECPSXMDisplay.cs new file mode 100644 index 00000000..5ff419c3 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/NECPSXMDisplay.cs @@ -0,0 +1,356 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Displays +{ + /// + /// + /// + public class NecPSXMDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, ICommunicationMonitor + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + + #region Command constants + public const string InputGetCmd = "\x01\x30\x41\x30\x43\x30\x36\x02\x30\x30\x36\x30\x03\x03\x0D"; + public const string Hdmi1Cmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x30\x30\x30\x31\x31\x03\x72\x0d"; + public const string Hdmi2Cmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x30\x30\x30\x31\x32\x03\x71\x0D"; + public const string Hdmi3Cmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x30\x30\x30\x38\x32\x03\x78\x0D"; + public const string Hdmi4Cmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x30\x30\x30\x38\x33\x03\x79\x0D"; + public const string Dp1Cmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x30\x30\x30\x30\x46\x03\x04\x0D"; + public const string Dp2Cmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x30\x30\x30\x31\x30\x03\x73\x0D"; + public const string Dvi1Cmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x30\x30\x30\x30\x33\x03\x71\x0d"; + public const string Video1Cmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x30\x30\x30\x30\x35\x03\x77\x0D"; + public const string VgaCmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x30\x30\x30\x30\x31\x03\x73\x0D"; + public const string RgbCmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x30\x30\x30\x30\x32\x03\x70\x0D"; + + public const string PowerOnCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x30\x33\x44\x36\x30\x30\x30\x31\x03\x73\x0D"; + public const string PowerOffCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x30\x33\x44\x36\x30\x30\x30\x34\x03\x76\x0D"; + public const string PowerToggleIrCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x31\x30\x30\x30\x30\x33\x30\x33\x03\x02\x0D"; + + public const string MuteOffCmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x38\x44\x30\x30\x30\x30\x03\x08\x0D"; + public const string MuteOnCmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x38\x44\x30\x30\x30\x31\x03\x09\x0D"; + public const string MuteToggleIrCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x31\x30\x30\x30\x31\x42\x30\x33\x03\x72\x0D"; + public const string MuteGetCmd = "\x01\x30\x41\x30\x43\x30\x36\x02\x30\x30\x38\x44\x03\x79\x0D"; + + public const string VolumeGetCmd = "\x01\x30\x41\x30\x43\x30\x36\x02\x30\x30\x36\x32\x03\x01\x0D"; + public const string VolumeLevelPartialCmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x30\x30\x36\x32"; //\x46\x46\x46\x46\x03\xNN\x0D + public const string VolumeUpCmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x31\x30\x41\x44\x30\x30\x30\x31\x03\x71\x0D"; + public const string VolumeDownCmd = "\x01\x30\x41\x30\x45\x30\x41\x02\x31\x30\x41\x44\x30\x30\x30\x32\x03\x72\x0D"; + + public const string MenuIrCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x31\x30\x30\x30\x32\x30\x30\x33\x03\x03\x0D"; + public const string UpIrCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x31\x30\x30\x30\x31\x35\x30\x33\x03\x05\x0D"; + public const string DownIrCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x31\x30\x30\x30\x31\x34\x30\x33\x03\x04\x0D"; + public const string LeftIrCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x31\x30\x30\x30\x32\x31\x30\x33\x03\x02\x0D"; + public const string RightIrCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x31\x30\x30\x30\x32\x32\x30\x33\x03\x01\x0D"; + public const string SelectIrCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x31\x30\x30\x30\x32\x33\x30\x33\x03\x00\x0D"; + public const string ExitIrCmd = "\x01\x30\x41\x30\x41\x30\x43\x02\x43\x32\x31\x30\x30\x30\x31\x46\x30\x33\x03\x76\x0D"; + #endregion + + bool _PowerIsOn; + bool _IsWarmingUp; + bool _IsCoolingDown; + ushort _VolumeLevel; + bool _IsMuted; + + protected override Func PowerIsOnFeedbackFunc { get { return () => _PowerIsOn; } } + protected override Func IsCoolingDownFeedbackFunc { get { return () => _IsCoolingDown; } } + protected override Func IsWarmingUpFeedbackFunc { get { return () => _IsWarmingUp; } } + protected override Func CurrentInputFeedbackFunc { get { return () => "Not Implemented"; } } + + + /// + /// Constructor for IBasicCommunication + /// + public NecPSXMDisplay(string key, string name, IBasicCommunication comm) + : base(key, name) + { + Communication = comm; + Init(); + } + /// + /// Constructor for TCP + /// + public NecPSXMDisplay(string key, string name, string hostname, int port) + : base(key, name) + { + Communication = new GenericTcpIpClient(key + "-tcp", hostname, port, 5000); + Init(); + } + + + /// + /// Constructor for COM + /// + public NecPSXMDisplay(string key, string name, ComPort port, ComPort.ComPortSpec spec) + : base(key, name) + { + Communication = new ComPortController(key + "-com", port, spec); + Init(); + } + + void Init() + { + PortGather = new CommunicationGather(Communication, '\x0d'); + PortGather.LineReceived += this.Port_LineReceived; + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "xx\x0d"); + + InputPorts.Add(new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi1), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi2), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi3), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.HdmiIn4, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi4), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.DisplayPortIn1, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DisplayPort, new Action(InputDisplayPort1), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.DisplayPortIn2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DisplayPort, new Action(InputDisplayPort2), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.DviIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Dvi, new Action(InputDvi1), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.CompositeIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Composite, new Action(InputVideo1), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.VgaIn, eRoutingSignalType.Video, + eRoutingPortConnectionType.Vga, new Action(InputVga), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.RgbIn, eRoutingSignalType.Video, + eRoutingPortConnectionType.Rgb, new Action(new Action(InputRgb)), this)); + + VolumeLevelFeedback = new IntFeedback(() => { return _VolumeLevel; }); + MuteFeedback = new BoolFeedback(() => _IsMuted); + + // new BoolCueActionPair(CommonBoolCue.Menu, b => { if(b) Send(MenuIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Up, b => { if(b) Send(UpIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Down, b => { if(b) Send(DownIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Left, b => { if(b) Send(LeftIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Right, b => { if(b) Send(RightIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Select, b => { if(b) Send(SelectIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Exit, b => { if(b) Send(ExitIrCmd); }), + //}; + } + + ~NecPSXMDisplay() + { + PortGather = null; + } + + public override bool CustomActivate() + { + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + return true; + } + + public override FeedbackCollection Feedbacks + { + get + { + var list = base.Feedbacks; + list.AddRange(new List + { + + }); + return list; + } + } + + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Received: '{0}'", ComTextHelper.GetEscapedText(args.Text)); + + if (args.Text=="DO SOMETHING HERE EVENTUALLY") + { + _IsMuted = true; + MuteFeedback.FireUpdate(); + } + } + + void AppendChecksumAndSend(string s) + { + int x = 0; + for (int i = 1; i < s.Length; i++) + x = x ^ s[i]; + + string send = s + (char)x + '\x0d'; + Send(send); + } + + void Send(string s) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Send: '{0}'", ComTextHelper.GetEscapedText(s)); + Communication.SendText(s); + } + + + public override void PowerOn() + { + Send(PowerOnCmd); + if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown) + { + _IsWarmingUp = true; + IsWarmingUpFeedback.FireUpdate(); + // Fake power-up cycle + WarmupTimer = new CTimer(o => + { + _IsWarmingUp = false; + _PowerIsOn = true; + IsWarmingUpFeedback.FireUpdate(); + PowerIsOnFeedback.FireUpdate(); + }, 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) + { + Send(PowerOffCmd); + _IsCoolingDown = true; + _PowerIsOn = false; + PowerIsOnFeedback.FireUpdate(); + IsCoolingDownFeedback.FireUpdate(); + // Fake cool-down cycle + CooldownTimer = new CTimer(o => + { + Debug.Console(2, this, "Cooldown timer ending"); + _IsCoolingDown = false; + IsCoolingDownFeedback.FireUpdate(); + }, CooldownTime); + } + } + + public override void PowerToggle() + { + if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue) + PowerOff(); + else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue) + PowerOn(); + } + + public void InputHdmi1() + { + Send(Hdmi1Cmd); + } + + public void InputHdmi2() + { + Send(Hdmi2Cmd); + } + + public void InputHdmi3() + { + Send(Hdmi3Cmd); + } + + public void InputHdmi4() + { + Send(Hdmi4Cmd); + } + + public void InputDisplayPort1() + { + Send(Dp1Cmd); + } + + public void InputDisplayPort2() + { + Send(Dp2Cmd); + } + + public void InputDvi1() + { + Send(Dvi1Cmd); + } + + public void InputVideo1() + { + Send(Video1Cmd); + } + + public void InputVga() + { + Send(VgaCmd); + } + + public void InputRgb() + { + Send(RgbCmd); + } + + public override void ExecuteSwitch(object selector) + { + if (selector is Action) + (selector as Action).Invoke(); + else + Debug.Console(1, this, "WARNING: ExecuteSwitch cannot handle type {0}", selector.GetType()); + //Send((string)selector); + } + + void SetVolume(ushort level) + { + var levelString = string.Format("{0}{1:X4}\x03", VolumeLevelPartialCmd, level); + AppendChecksumAndSend(levelString); + //Debug.Console(2, this, "Volume:{0}", ComTextHelper.GetEscapedText(levelString)); + _VolumeLevel = level; + VolumeLevelFeedback.FireUpdate(); + } + + #region IBasicVolumeWithFeedback Members + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public BoolFeedback MuteFeedback { get; private set; } + + public void MuteOff() + { + Send(MuteOffCmd); + } + + public void MuteOn() + { + Send(MuteOnCmd); + } + + void IBasicVolumeWithFeedback.SetVolume(ushort level) + { + SetVolume(level); + } + + #endregion + + #region IBasicVolumeControls Members + + public void MuteToggle() + { + Send(MuteToggleIrCmd); + } + + public void VolumeDown(bool pressRelease) + { + //throw new NotImplementedException(); +//#warning need incrementer for these + SetVolume(_VolumeLevel++); + } + + public void VolumeUp(bool pressRelease) + { + //throw new NotImplementedException(); + SetVolume(_VolumeLevel--); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/NecPaSeriesProjector.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/NecPaSeriesProjector.cs new file mode 100644 index 00000000..7888bace --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/NecPaSeriesProjector.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Essentials.Core; +using PepperDash.Core; + +namespace PepperDash.Essentials.Devices.Displays +{ + public class NecPaSeriesProjector : ComTcpDisplayBase + { + public readonly IntFeedback Lamp1RemainingPercent; + int _Lamp1RemainingPercent; + public readonly IntFeedback Lamp2RemainingPercent; + int _Lamp2RemainingPercent; + protected override Func PowerIsOnFeedbackFunc + { + get { return () => _PowerIsOn; } + } + bool _PowerIsOn; + + protected override Func IsCoolingDownFeedbackFunc + { + get { return () => false; } + } + + protected override Func IsWarmingUpFeedbackFunc + { + get { return () => false; } + } + + public override void PowerToggle() + { + throw new NotImplementedException(); + } + + public override void ExecuteSwitch(object selector) + { + throw new NotImplementedException(); + } + + Dictionary InputMap; + + /// + /// Constructor + /// + public NecPaSeriesProjector(string key, string name) + : base(key, name) + { + Lamp1RemainingPercent = new IntFeedback("Lamp1RemainingPercent", () => _Lamp1RemainingPercent); + Lamp2RemainingPercent = new IntFeedback("Lamp2RemainingPercent", () => _Lamp2RemainingPercent); + + InputMap = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "computer1", "\x02\x03\x00\x00\x02\x01\x01\x09" }, + { "computer2", "\x02\x03\x00\x00\x02\x01\x02\x0a" }, + { "computer3", "\x02\x03\x00\x00\x02\x01\x03\x0b" }, + { "hdmi", "\x02\x03\x00\x00\x02\x01\x1a\x22" }, + { "dp", "\x02\x03\x00\x00\x02\x01\x1b\x23" }, + { "video", "\x02\x03\x00\x00\x02\x01\x06\x0e" }, + { "viewer", "\x02\x03\x00\x00\x02\x01\x1f\x27" }, + { "network", "\x02\x03\x00\x00\x02\x01\x20\x28" }, + }; + } + + void IsConnected_OutputChange(object sender, EventArgs e) + { + + } + + public void SetEnable(bool state) + { + var tcp = CommunicationMethod as GenericTcpIpClient; + if (tcp != null) + { + tcp.Connect(); + } + } + + public override void PowerOn() + { + SendText("\x02\x00\x00\x00\x00\x02"); + } + + public override void PowerOff() + { + SendText("\x02\x01\x00\x00\x00\x03"); + } + + public void PictureMuteOn() + { + SendText("\x02\x10\x00\x00\x00\x12"); + } + + public void PictureMuteOff() + { + SendText("\x02\x11\x00\x00\x00\x13"); + } + + public void GetRunningStatus() + { + SendText("\x00\x85\x00\x00\x01\x01\x87"); + } + + public void GetLampRemaining(int lampNum) + { + if (!_PowerIsOn) return; + + var bytes = new byte[]{0x03,0x96,0x00,0x00,0x02,0x00,0x04}; + if (lampNum == 2) + bytes[5] = 0x01; + SendBytes(AppendChecksum(bytes)); + } + + public void SelectInput(string inputKey) + { + if (InputMap.ContainsKey(inputKey)) + SendText(InputMap[inputKey]); + } + + void SendText(string text) + { + if (CommunicationMethod != null) + CommunicationMethod.SendText(text); + } + + void SendBytes(byte[] bytes) + { + if (CommunicationMethod != null) + CommunicationMethod.SendBytes(bytes); + } + + byte[] AppendChecksum(byte[] bytes) + { + byte sum = unchecked((byte)bytes.Sum(x => (int)x)); + var retVal = new byte[bytes.Length + 1]; + bytes.CopyTo(retVal, 0); + retVal[retVal.Length - 1] = sum; + return retVal; + } + + protected override void CommunicationMethod_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs args) + { + var bytes = args.Bytes; + ParseBytes(args.Bytes); + } + + void ParseBytes(byte[] bytes) + { + if (bytes[0] == 0x22) + { + // Power on + if (bytes[1] == 0x00) + { + _PowerIsOn = true; + PowerIsOnFeedback.FireUpdate(); + } + // Power off + else if (bytes[1] == 0x01) + { + _PowerIsOn = false; + PowerIsOnFeedback.FireUpdate(); + } + } + // Running Status + else if (bytes[0] == 0x20 && bytes[1] == 0x85 && bytes[4] == 0x10) + { + var operationStates = new Dictionary + { + { 0x00, "Standby" }, + { 0x04, "Power On" }, + { 0x05, "Cooling" }, + { 0x06, "Standby (error)" }, + { 0x0f, "Standby (power saving" }, + { 0x10, "Network Standby" }, + { 0xff, "Not supported" } + }; + + var newPowerIsOn = bytes[7] == 0x01; + if (newPowerIsOn != _PowerIsOn) + { + _PowerIsOn = newPowerIsOn; + PowerIsOnFeedback.FireUpdate(); + } + + Debug.Console(2, this, "PowerIsOn={0}\rCooling={1}\rPowering on/off={2}\rStatus={3}", + _PowerIsOn, + bytes[8] == 0x01, + bytes[9] == 0x01, + operationStates[bytes[10]]); + + } + // Lamp remaining + else if (bytes[0] == 0x23 && bytes[1] == 0x96 && bytes[4] == 0x06 && bytes[6] == 0x04) + { + var newValue = bytes[7]; + if (bytes[5] == 0x00) + { + if (newValue != _Lamp1RemainingPercent) + { + _Lamp1RemainingPercent = newValue; + Lamp1RemainingPercent.FireUpdate(); + } + } + else + { + if (newValue != _Lamp2RemainingPercent) + { + _Lamp2RemainingPercent = newValue; + Lamp2RemainingPercent.FireUpdate(); + } + } + Debug.Console(0, this, "Lamp {0}, {1}% remaining", (bytes[5] + 1), bytes[7]); + } + + } + + + #region INonStandardControls Members + + public Dictionary> GetNonStandardControls() + { + return new Dictionary> + { + { CommonBoolCue.PowerOn, o => PowerOn() }, + { CommonBoolCue.PowerOff, o => PowerOff() }, + { Cue.BoolCue("PictureMute", 0), o => + { + if((bool)o) + PictureMuteOn(); + else + PictureMuteOff(); } }, + { Cue.UShortCue("GetLampRemaining", 0), o => GetLampRemaining((int) o) }, + { Cue.StringCue("SelectInput", 0), o => SelectInput((String)o) } + }; + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/PanasonicThDisplay.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/PanasonicThDisplay.cs new file mode 100644 index 00000000..01d3713d --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/PanasonicThDisplay.cs @@ -0,0 +1,338 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Displays +{ + /// + /// + /// + public class PanasonicThefDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, ICommunicationMonitor + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + + #region Command constants + public const string InputGetCmd = "\x02QMI\x03"; + public const string Hdmi1Cmd = "\x02IMS:HM1\x03"; + public const string Hdmi2Cmd = "\x02IMS:HM2\x03"; + public const string Hdmi3Cmd = ""; + public const string Hdmi4Cmd = ""; + public const string Dp1Cmd = ""; + public const string Dp2Cmd = ""; + public const string Dvi1Cmd = "\x02IMS:DV1"; + public const string Video1Cmd = ""; + public const string VgaCmd = ""; + public const string RgbCmd = ""; + + public const string PowerOnCmd = "\x02PON\x03"; + public const string PowerOffCmd = "\x02POF\x03"; + public const string PowerToggleIrCmd = ""; + + public const string MuteOffCmd = "\x02AMT:0\x03"; + public const string MuteOnCmd = "\x02AMT:1\x03"; + public const string MuteToggleIrCmd = "\x02AMT\x03"; + public const string MuteGetCmd = "\x02QAM\x03"; + + public const string VolumeGetCmd = "\x02QAV\x03"; + public const string VolumeLevelPartialCmd = "\x02AVL:"; // + public const string VolumeUpCmd = "\x02AUU\x03"; + public const string VolumeDownCmd = "\x02AUD\x03"; + + public const string MenuIrCmd = ""; + public const string UpIrCmd = ""; + public const string DownIrCmd = ""; + public const string LeftIrCmd = ""; + public const string RightIrCmd = ""; + public const string SelectIrCmd = ""; + public const string ExitIrCmd = ""; + #endregion + + bool _PowerIsOn; + bool _IsWarmingUp; + bool _IsCoolingDown; + ushort _VolumeLevel; + bool _IsMuted; + + protected override Func PowerIsOnFeedbackFunc { get { return () => _PowerIsOn; } } + protected override Func IsCoolingDownFeedbackFunc { get { return () => _IsCoolingDown; } } + protected override Func IsWarmingUpFeedbackFunc { get { return () => _IsWarmingUp; } } + protected override Func CurrentInputFeedbackFunc { get { return () => "Not Implemented"; } } + + + /// + /// Constructor for IBasicCommunication + /// + public PanasonicThefDisplay(string key, string name, IBasicCommunication comm) + : base(key, name) + { + Communication = comm; + Init(); + } + /// + /// Constructor for TCP + /// + public PanasonicThefDisplay(string key, string name, string hostname, int port) + : base(key, name) + { + Communication = new GenericTcpIpClient(key + "-tcp", hostname, port, 5000); + Init(); + } + + + /// + /// Constructor for COM + /// + public PanasonicThefDisplay(string key, string name, ComPort port, ComPort.ComPortSpec spec) + : base(key, name) + { + Communication = new ComPortController(key + "-com", port, spec); + Init(); + } + + void Init() + { + PortGather = new CommunicationGather(Communication, '\x0d'); + PortGather.LineReceived += this.Port_LineReceived; + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "\x02QPW\x03"); // Query Power + + InputPorts.Add(new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi1), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi2), this)); + + InputPorts.Add(new RoutingInputPort(RoutingPortNames.DviIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Dvi, new Action(InputDvi1), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.CompositeIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Composite, new Action(InputVideo1), this)); + InputPorts.Add(new RoutingInputPort(RoutingPortNames.VgaIn, eRoutingSignalType.Video, + eRoutingPortConnectionType.Vga, new Action(InputVga), this)); + + + VolumeLevelFeedback = new IntFeedback(() => { return _VolumeLevel; }); + MuteFeedback = new BoolFeedback(() => _IsMuted); + + // new BoolCueActionPair(CommonBoolCue.Menu, b => { if(b) Send(MenuIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Up, b => { if(b) Send(UpIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Down, b => { if(b) Send(DownIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Left, b => { if(b) Send(LeftIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Right, b => { if(b) Send(RightIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Select, b => { if(b) Send(SelectIrCmd); }), + // new BoolCueActionPair(CommonBoolCue.Exit, b => { if(b) Send(ExitIrCmd); }), + //}; + } + + ~PanasonicThefDisplay() + { + PortGather = null; + } + + public override bool CustomActivate() + { + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + return true; + } + + public override FeedbackCollection Feedbacks + { + get + { + var list = base.Feedbacks; + list.AddRange(new List + { + + }); + return list; + } + } + + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Received: '{0}'", ComTextHelper.GetEscapedText(args.Text)); + + if (args.Text=="DO SOMETHING HERE EVENTUALLY") + { + _IsMuted = true; + MuteFeedback.FireUpdate(); + } + } + + void Send(string s) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Send: '{0}'", ComTextHelper.GetEscapedText(s)); + Communication.SendText(s); + } + + + public override void PowerOn() + { + Send(PowerOnCmd); + if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown) + { + _IsWarmingUp = true; + IsWarmingUpFeedback.FireUpdate(); + // Fake power-up cycle + WarmupTimer = new CTimer(o => + { + _IsWarmingUp = false; + _PowerIsOn = true; + IsWarmingUpFeedback.FireUpdate(); + PowerIsOnFeedback.FireUpdate(); + }, 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) + { + Send(PowerOffCmd); + _IsCoolingDown = true; + _PowerIsOn = false; + PowerIsOnFeedback.FireUpdate(); + IsCoolingDownFeedback.FireUpdate(); + // Fake cool-down cycle + CooldownTimer = new CTimer(o => + { + Debug.Console(2, this, "Cooldown timer ending"); + _IsCoolingDown = false; + IsCoolingDownFeedback.FireUpdate(); + }, CooldownTime); + } + } + + public override void PowerToggle() + { + if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue) + PowerOff(); + else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue) + PowerOn(); + } + + public void InputHdmi1() + { + Send(Hdmi1Cmd); + } + + public void InputHdmi2() + { + Send(Hdmi2Cmd); + } + + public void InputHdmi3() + { + Send(Hdmi3Cmd); + } + + public void InputHdmi4() + { + Send(Hdmi4Cmd); + } + + public void InputDisplayPort1() + { + Send(Dp1Cmd); + } + + public void InputDisplayPort2() + { + Send(Dp2Cmd); + } + + public void InputDvi1() + { + Send(Dvi1Cmd); + } + + public void InputVideo1() + { + Send(Video1Cmd); + } + + public void InputVga() + { + Send(VgaCmd); + } + + public void InputRgb() + { + Send(RgbCmd); + } + + public override void ExecuteSwitch(object selector) + { + if (selector is Action) + (selector as Action).Invoke(); + else + Debug.Console(1, this, "WARNING: ExecuteSwitch cannot handle type {0}", selector.GetType()); + //Send((string)selector); + } + + void SetVolume(ushort level) + { + var levelString = string.Format("{0}{1:X3}\x03", VolumeLevelPartialCmd, level); + + //Debug.Console(2, this, "Volume:{0}", ComTextHelper.GetEscapedText(levelString)); + _VolumeLevel = level; + VolumeLevelFeedback.FireUpdate(); + } + + #region IBasicVolumeWithFeedback Members + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public BoolFeedback MuteFeedback { get; private set; } + + public void MuteOff() + { + Send(MuteOffCmd); + } + + public void MuteOn() + { + Send(MuteOnCmd); + } + + void IBasicVolumeWithFeedback.SetVolume(ushort level) + { + SetVolume(level); + } + + #endregion + + #region IBasicVolumeControls Members + + public void MuteToggle() + { + Send(MuteToggleIrCmd); + } + + public void VolumeDown(bool pressRelease) + { + //throw new NotImplementedException(); +//#warning need incrementer for these + SetVolume(_VolumeLevel++); + } + + public void VolumeUp(bool pressRelease) + { + //throw new NotImplementedException(); + SetVolume(_VolumeLevel--); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/SamsungMDCDisplay.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/SamsungMDCDisplay.cs new file mode 100644 index 00000000..c7d8d0d5 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Display/SamsungMDCDisplay.cs @@ -0,0 +1,620 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.CrestronThread; +using Crestron.SimplSharpPro; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Displays +{ + /// + /// + /// + public class SamsungMDC : TwoWayDisplayBase, IBasicVolumeWithFeedback, ICommunicationMonitor, IInputDisplayPort1, IInputDisplayPort2, + IInputHdmi1, IInputHdmi2, IInputHdmi3, IInputHdmi4 + { + public IBasicCommunication Communication { get; private set; } + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + public byte ID { get; private set; } + + bool LastCommandSentWasVolume; + + bool _PowerIsOn; + bool _IsWarmingUp; + bool _IsCoolingDown; + ushort _VolumeLevelForSig; + int _LastVolumeSent; + bool _IsMuted; + RoutingInputPort _CurrentInputPort; + byte[] IncomingBuffer = new byte[]{}; + ActionIncrementer VolumeIncrementer; + bool VolumeIsRamping; + public bool IsInStandby { get; private set; } + bool IsPoweringOnIgnorePowerFb; + + protected override Func PowerIsOnFeedbackFunc { get { return () => _PowerIsOn; } } + protected override Func IsCoolingDownFeedbackFunc { get { return () => _IsCoolingDown; } } + protected override Func IsWarmingUpFeedbackFunc { get { return () => _IsWarmingUp; } } + protected override Func CurrentInputFeedbackFunc { get { return () => _CurrentInputPort.Key; } } + + /// + /// Constructor for IBasicCommunication + /// + public SamsungMDC(string key, string name, IBasicCommunication comm, string id) + : base(key, name) + { + Communication = comm; + Communication.BytesReceived += new EventHandler(Communication_BytesReceived); + + ID = id == null ? (byte)0x01 : Convert.ToByte(id, 16); // If id is null, set default value of 0x01, otherwise assign value passed in constructor + Init(); + } + + /// + /// Constructor for TCP + /// + public SamsungMDC(string key, string name, string hostname, int port, string id) + : base(key, name) + { + Communication = new GenericTcpIpClient(key + "-tcp", hostname, port, 5000); + ID = id == null ? (byte)0x01 : Convert.ToByte(id, 16); // If id is null, set default value of 0x01, otherwise assign value passed in constructor + Init(); + } + + /// + /// Constructor for COM + /// + public SamsungMDC(string key, string name, ComPort port, ComPort.ComPortSpec spec, string id) + : base(key, name) + { + Communication = new ComPortController(key + "-com", port, spec); + //Communication.TextReceived += new EventHandler(Communication_TextReceived); + + ID = id == null ? (byte)0x01 : Convert.ToByte(id, 16); // If id is null, set default value of 0x01, otherwise assign value passed in constructor + Init(); + } + + void AddRoutingInputPort(RoutingInputPort port, byte fbMatch) + { + port.FeedbackMatchObject = fbMatch; + InputPorts.Add(port); + } + + void Init() + { + WarmupTime = 10000; + CooldownTime = 8000; + + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 2000, 120000, 300000, StatusGet); + DeviceManager.AddDevice(CommunicationMonitor); + + VolumeIncrementer = new ActionIncrementer(655, 0, 65535, 800, 80, + v => SetVolume((ushort)v), + () => _LastVolumeSent); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi1), this), 0x21); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn1PC, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi1PC), this), 0x22); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi2), this), 0x23); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn2PC, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi2PC), this), 0x24); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(InputHdmi3), this), 0x32); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.DisplayPortIn1, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.DisplayPort, new Action(InputDisplayPort1), this), 0x25); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.DviIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Dvi, new Action(InputDvi1), this), 0x18); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.CompositeIn, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Composite, new Action(InputVideo1), this), 0x08); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.RgbIn1, eRoutingSignalType.Video, + eRoutingPortConnectionType.Vga, new Action(InputRgb1), this), 0x14); + + AddRoutingInputPort(new RoutingInputPort(RoutingPortNames.RgbIn2, eRoutingSignalType.Video, + eRoutingPortConnectionType.Rgb, new Action(new Action(InputRgb2)), this), 0x1E); + + VolumeLevelFeedback = new IntFeedback(() => { return _VolumeLevelForSig; }); + MuteFeedback = new BoolFeedback(() => _IsMuted); + + StatusGet(); + } + + /// + /// + /// + /// + public override bool CustomActivate() + { + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + return true; + } + + public override FeedbackCollection Feedbacks + { + get + { + var list = base.Feedbacks; + list.AddRange(new List + { + VolumeLevelFeedback, + MuteFeedback, + CurrentInputFeedback + }); + return list; + } + } + + /// + /// / + /// + /// + 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++) + { + if (newBytes[i] == 0xAA && newBytes[i + 1] == 0xFF) + { + 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) + { + 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) + { + 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; + + case 0x11: + UpdatePowerFB(message[2]); + break; + + case 0x12: + UpdateVolumeFB(message[2]); + break; + + case 0x13: + UpdateMuteFb(message[2]); + break; + + case 0x14: + UpdateInputFb(message[2]); + break; + + default: + break; + } + } + // 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; + } + + /// + /// + /// + void UpdatePowerFB(byte powerByte) + { + var newVal = powerByte == 1; + if (newVal != _PowerIsOn) + { + _PowerIsOn = newVal; + PowerIsOnFeedback.FireUpdate(); + } + } + + /// + /// Updates power status from general updates where source is included. + /// Compensates for errant standby / power off hiccups by ignoring + /// power off states with input < 0x10 + /// + void UpdatePowerFB(byte powerByte, byte inputByte) + { + // This should reject errant power feedbacks when switching away from input on standby. + if (powerByte == 0x01 && inputByte < 0x10) + IsInStandby = true; + if (powerByte == 0x00 && IsInStandby) // Ignore power off if coming from standby - glitch + { + IsInStandby = false; + return; + } + + UpdatePowerFB(powerByte); + } + + /// + /// + /// + void UpdateVolumeFB(byte b) + { + var newVol = (ushort)NumericalHelpers.Scale((double)b, 0, 100, 0, 65535); + if (!VolumeIsRamping) + _LastVolumeSent = newVol; + if (newVol != _VolumeLevelForSig) + { + _VolumeLevelForSig = newVol; + VolumeLevelFeedback.FireUpdate(); + } + } + + /// + /// + /// + void UpdateMuteFb(byte b) + { + var newMute = b == 1; + if (newMute != _IsMuted) + { + _IsMuted = newMute; + MuteFeedback.FireUpdate(); + } + } + + /// + /// + /// + void UpdateInputFb(byte b) + { + var newInput = InputPorts.FirstOrDefault(i => i.FeedbackMatchObject.Equals(b)); + if (newInput != null && newInput != _CurrentInputPort) + { + _CurrentInputPort = newInput; + CurrentInputFeedback.FireUpdate(); + } + } + + /// + /// Formats an outgoing message. Replaces third byte with ID and replaces last byte with checksum + /// + /// + void SendBytes(byte[] b) + { + if (LastCommandSentWasVolume) // If the last command sent was volume + if (b[1] != 0x12) // Check if this command is volume, and if not, delay this command + CrestronEnvironment.Sleep(100); + + b[2] = ID; + // append checksum by adding all bytes, except last which should be 00 + int checksum = 0; + for (var i = 1; i < b.Length - 1; i++) // add 2nd through 2nd-to-last bytes + { + checksum += b[i]; + } + checksum = checksum & 0x000000FF; // mask off MSBs + b[b.Length - 1] = (byte)checksum; + 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, "Sending:{0}", ComTextHelper.GetEscapedText(b)); + + if (b[1] == 0x12) + LastCommandSentWasVolume = true; + else + LastCommandSentWasVolume = false; + + Communication.SendBytes(b); + } + + + /// + /// + /// + public void StatusGet() + { + SendBytes(new byte[] { 0xAA, 0x00, 0x00, 0x00, 0x00 }); + } + + /// + /// + /// + public override void PowerOn() + { + IsPoweringOnIgnorePowerFb = true; + //Send(PowerOnCmd); + SendBytes(new byte[] { 0xAA, 0x11, 0x00, 0x01, 0x01, 0x00 }); + if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown) + { + _IsWarmingUp = true; + IsWarmingUpFeedback.FireUpdate(); + // Fake power-up cycle + WarmupTimer = new CTimer(o => + { + _IsWarmingUp = false; + _PowerIsOn = true; + IsWarmingUpFeedback.FireUpdate(); + PowerIsOnFeedback.FireUpdate(); + }, WarmupTime); + } + } + + /// + /// + /// + public override void PowerOff() + { + IsPoweringOnIgnorePowerFb = false; + // If a display has unreliable-power off feedback, just override this and + // remove this check. + if (!_IsWarmingUp && !_IsCoolingDown) // PowerIsOnFeedback.BoolValue && + { + //Send(PowerOffCmd); + SendBytes(new byte[] { 0xAA, 0x11, 0x00, 0x01, 0x00, 0x00 }); + _IsCoolingDown = true; + _PowerIsOn = false; + PowerIsOnFeedback.FireUpdate(); + IsCoolingDownFeedback.FireUpdate(); + // Fake cool-down cycle + CooldownTimer = new CTimer(o => + { + _IsCoolingDown = false; + IsCoolingDownFeedback.FireUpdate(); + }, CooldownTime); + } + } + + public override void PowerToggle() + { + if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue) + PowerOff(); + else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue) + PowerOn(); + } + + public void PowerGet() + { + SendBytes(new byte[] { 0xAA, 0x11, 0x00, 0x00, 0x00 }); + } + + public void InputHdmi1() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x21, 0x00 }); + } + + public void InputHdmi1PC() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x22, 0x00 }); + } + + public void InputHdmi2() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x23, 0x00 }); + } + + public void InputHdmi2PC() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x24, 0x00 }); + } + + public void InputHdmi3() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x32, 0x00 }); + } + + public void InputHdmi4() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x34, 0x00 }); + } + + public void InputDisplayPort1() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x25, 0x00 }); + } + + public void InputDisplayPort2() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x26, 0x00 }); + } + + public void InputDvi1() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x18, 0x00 }); + } + + public void InputVideo1() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x08, 0x00 }); + } + + public void InputRgb1() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x14, 0x00 }); + } + + public void InputRgb2() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x01, 0x1E, 0x00 }); + } + + public void InputGet() + { + SendBytes(new byte[] { 0xAA, 0x14, 0x00, 0x00, 0x00 }); + } + + + /// + /// Executes a switch, turning on display if necessary. + /// + /// + public override void ExecuteSwitch(object selector) + { + //if (!(selector is Action)) + // Debug.Console(1, this, "WARNING: ExecuteSwitch cannot handle type {0}", selector.GetType()); + + if (_PowerIsOn) + (selector as Action)(); + else // if power is off, wait until we get on FB to send it. + { + // One-time event handler to wait for power on before executing switch + EventHandler handler = null; // necessary to allow reference inside lambda to handler + handler = (o, a) => + { + if (!_IsWarmingUp) // Done warming + { + IsWarmingUpFeedback.OutputChange -= handler; + (selector as Action)(); + } + }; + IsWarmingUpFeedback.OutputChange += handler; // attach and wait for on FB + PowerOn(); + } + } + + /// + /// Scales the level to the range of the display and sends the command + /// + /// + public void SetVolume(ushort level) + { + _LastVolumeSent = level; + var scaled = (int)NumericalHelpers.Scale(level, 0, 65535, 0, 100); + // The inputs to Scale ensure that byte won't overflow + SendBytes(new byte[] { 0xAA, 0x12, 0x00, 0x01, Convert.ToByte(scaled), 0x00 }); + } + + #region IBasicVolumeWithFeedback Members + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public BoolFeedback MuteFeedback { get; private set; } + + /// + /// + /// + public void MuteOff() + { + SendBytes(new byte[] { 0xAA, 0x13, 0x00, 0x01, 0x00, 0x00 }); + } + + /// + /// + /// + public void MuteOn() + { + SendBytes(new byte[] { 0xAA, 0x13, 0x00, 0x01, 0x01, 0x00 }); + } + + /// + /// + /// + public void MuteGet() + { + SendBytes(new byte[] { 0xAA, 0x13, 0x00, 0x00, 0x00 }); + } + + #endregion + + #region IBasicVolumeControls Members + + /// + /// + /// + public void MuteToggle() + { + if (_IsMuted) + MuteOff(); + else + MuteOn(); + } + + /// + /// + /// + /// + public void VolumeDown(bool pressRelease) + { + if (pressRelease) + { + VolumeIncrementer.StartDown(); + VolumeIsRamping = true; + } + else + { + VolumeIsRamping = false; + VolumeIncrementer.Stop(); + } + } + + /// + /// + /// + /// + public void VolumeUp(bool pressRelease) + { + if (pressRelease) + { + VolumeIncrementer.StartUp(); + VolumeIsRamping = true; + } + else + { + VolumeIsRamping = false; + VolumeIncrementer.Stop(); + } + } + + /// + /// + /// + public void VolumeGet() + { + SendBytes(new byte[] { 0xAA, 0x12, 0x00, 0x00, 0x00 }); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Environment/Crestron Lighting/Din8sw8.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Environment/Crestron Lighting/Din8sw8.cs new file mode 100644 index 00000000..e371d2d1 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Environment/Crestron Lighting/Din8sw8.cs @@ -0,0 +1,88 @@ +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.Lighting; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.CrestronIO; + +namespace PepperDash.Essentials.Devices.Common.Environment.Lighting +{ + public class Din8sw8Controller : Device, ISwitchedOutputCollection + { + // Need to figure out some sort of interface to make these switched outputs behave like processor relays so they can be used interchangably + + public Din8Sw8 SwitchModule { get; private set; } + + /// + /// Collection of generic switched outputs + /// + public Dictionary SwitchedOutputs { get; private set; } + + public Din8sw8Controller(string key, uint cresnetId) + : base(key) + { + SwitchedOutputs = new Dictionary(); + + SwitchModule = new Din8Sw8(cresnetId, Global.ControlSystem); + + if (SwitchModule.Register() != eDeviceRegistrationUnRegistrationResponse.Success) + { + Debug.Console(2, this, "Error registering Din8sw8. Reason: {0}", SwitchModule.RegistrationFailureReason); + } + + PopulateDictionary(); + } + + public override bool CustomActivate() + { + + + return base.CustomActivate(); + } + + /// + /// Populates the generic collection with the loads from the Crestron collection + /// + void PopulateDictionary() + { + foreach (var item in SwitchModule.SwitchedLoads) + { + SwitchedOutputs.Add(item.Number, new Din8sw8Output(item)); + } + } + } + + /// + /// Wrapper class to + /// + public class Din8sw8Output : ISwitchedOutput + { + SwitchedLoadWithOverrideParameter SwitchedOutput; + + public BoolFeedback OutputIsOnFeedback { get; protected set; } + + public Din8sw8Output(SwitchedLoadWithOverrideParameter switchedOutput) + { + SwitchedOutput = switchedOutput; + + OutputIsOnFeedback = new BoolFeedback(new Func(() => SwitchedOutput.IsOn)); + } + + public void On() + { + SwitchedOutput.FullOn(); + } + + public void Off() + { + SwitchedOutput.FullOff(); + } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Environment/Lutron/LutronQuantum.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Environment/Lutron/LutronQuantum.cs new file mode 100644 index 00000000..adcceb4f --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Environment/Lutron/LutronQuantum.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Lighting; + +namespace PepperDash.Essentials.Devices.Common.Environment.Lutron +{ + public class LutronQuantumArea : LightingBase, ILightingMasterRaiseLower, ICommunicationMonitor + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + + CTimer SubscribeAfterLogin; + + public string IntegrationId; + string Username; + string Password; + + const string Delimiter = "\x0d\x0a"; + const string Set = "#"; + const string Get = "?"; + + public LutronQuantumArea(string key, string name, IBasicCommunication comm, LutronQuantumPropertiesConfig props) + : base(key, name) + { + Communication = comm; + + IntegrationId = props.IntegrationId; + + if (props.Control.Method != eControlMethod.Com) + { + + Username = props.Control.TcpSshProperties.Username; + Password = props.Control.TcpSshProperties.Password; + } + + + LightingScenes = props.Scenes; + + var socket = comm as ISocketStatus; + if (socket != null) + { + // IP Control + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + else + { + // RS-232 Control + } + + Communication.TextReceived += new EventHandler(Communication_TextReceived); + + PortGather = new CommunicationGather(Communication, Delimiter); + PortGather.LineReceived += new EventHandler(PortGather_LineReceived); + + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 120000, 120000, 300000, "?ETHERNET,0\x0d\x0a"); + } + } + + public override bool CustomActivate() + { + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + + return true; + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString()); + + if (e.Client.IsConnected) + { + // Tasks on connect + } + } + + /// + /// Checks for responses that do not contain the delimiter + /// + /// + /// + void Communication_TextReceived(object sender, GenericCommMethodReceiveTextArgs args) + { + Debug.Console(2, this, "Text Received: '{0}'", args.Text); + + if (args.Text.Contains("login:")) + { + // Login + SendLine(Username); + } + else if (args.Text.Contains("password:")) + { + // Login + SendLine(Password); + SubscribeAfterLogin = new CTimer(x => SubscribeToFeedback(), null, 5000); + + } + else if (args.Text.Contains("Access Granted")) + { + if (SubscribeAfterLogin != null) + { + SubscribeAfterLogin.Stop(); + } + SubscribeToFeedback(); + } + } + + /// + /// Handles all responses that contain the delimiter + /// + /// + /// + void PortGather_LineReceived(object sender, GenericCommMethodReceiveTextArgs args) + { + Debug.Console(2, this, "Line Received: '{0}'", args.Text); + + try + { + if (args.Text.Contains("~AREA")) + { + var response = args.Text.Split(','); + + var integrationId = response[1]; + + if (integrationId != IntegrationId) + { + Debug.Console(2, this, "Response is not for correct Integration ID"); + return; + } + else + { + var action = Int32.Parse(response[2]); + + switch (action) + { + case (int)eAction.Scene: + { + var scene = response[3]; + CurrentLightingScene = LightingScenes.FirstOrDefault(s => s.ID.Equals(scene)); + + OnLightingSceneChange(); + + break; + } + default: + break; + } + } + } + } + catch (Exception e) + { + Debug.Console(2, this, "Error parsing response:\n{0}", e); + } + } + + /// + /// Subscribes to feedback + /// + public void SubscribeToFeedback() + { + Debug.Console(1, "Sending Monitoring Subscriptions"); + SendLine("#MONITORING,6,1"); + SendLine("#MONITORING,8,1"); + SendLine("#MONITORING,5,2"); + } + + /// + /// Recalls the specified scene + /// + /// + /// + + public override void SelectScene(LightingScene scene) + { + Debug.Console(1, this, "Selecting Scene: '{0}'", scene.Name); + SendLine(string.Format("{0}AREA,{1},{2},{3}", Set, IntegrationId, (int)eAction.Scene, scene.ID)); + } + + /// + /// Begins raising the lights in the area + /// + public void MasterRaise() + { + SendLine(string.Format("{0}AREA,{1},{2}", Set, IntegrationId, (int)eAction.Raise)); + } + + /// + /// Begins lowering the lights in the area + /// + public void MasterLower() + { + SendLine(string.Format("{0}AREA,{1},{2}", Set, IntegrationId, (int)eAction.Lower)); + } + + /// + /// Stops the current raise/lower action + /// + public void MasterRaiseLowerStop() + { + SendLine(string.Format("{0}AREA,{1},{2}", Set, IntegrationId, (int)eAction.Stop)); + } + + /// + /// Appends the delimiter and sends the string + /// + /// + public void SendLine(string s) + { + Debug.Console(2, this, "TX: '{0}'", s); + Communication.SendText(s + Delimiter); + } + } + + public enum eAction : int + { + SetLevel = 1, + Raise = 2, + Lower = 3, + Stop = 4, + Scene = 6, + DaylightMode = 7, + OccupancyState = 8, + OccupancyMode = 9, + OccupiedLevelOrScene = 12, + UnoccupiedLevelOrScene = 13, + HyperionShaddowSensorOverrideState = 26, + HyperionBrightnessSensorOverrideStatue = 27 + } + + public class LutronQuantumPropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + public ControlPropertiesConfig Control { get; set; } + + public string IntegrationId { get; set; } + public List Scenes { get; set; } + + // Moved to use existing properties in Control object + // public string Username { get; set; } + // public string Password { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Environment/Somfy/RelayControlledShade.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Environment/Somfy/RelayControlledShade.cs new file mode 100644 index 00000000..22e06d03 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Environment/Somfy/RelayControlledShade.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.CrestronIO; +using PepperDash.Essentials.Core.Shades; + +namespace PepperDash.Essentials.Devices.Common.Environment.Somfy +{ + /// + /// Controls a single shade using three relays + /// + public class RelayControlledShade : ShadeBase, IShadesOpenCloseStop + { + RelayControlledShadeConfigProperties Config; + + ISwitchedOutput OpenRelay; + ISwitchedOutput StopOrPresetRelay; + ISwitchedOutput CloseRelay; + + int RelayPulseTime; + + public string StopOrPresetButtonLabel { get; set; } + + public RelayControlledShade(string key, string name, RelayControlledShadeConfigProperties config) + : base(key, name) + { + Config = config; + + RelayPulseTime = Config.RelayPulseTime; + + StopOrPresetButtonLabel = Config.StopOrPresetLabel; + + } + + public override bool CustomActivate() + { + //Create ISwitchedOutput objects based on props + OpenRelay = GetSwitchedOutputFromDevice(Config.Relays.Open); + StopOrPresetRelay = GetSwitchedOutputFromDevice(Config.Relays.StopOrPreset); + CloseRelay = GetSwitchedOutputFromDevice(Config.Relays.Close); + + + return base.CustomActivate(); + } + + public override void Open() + { + Debug.Console(1, this, "Opening Shade: '{0}'", this.Name); + + PulseOutput(OpenRelay, RelayPulseTime); + } + + public override void StopOrPreset() + { + Debug.Console(1, this, "Stopping or recalling preset on Shade: '{0}'", this.Name); + + PulseOutput(StopOrPresetRelay, RelayPulseTime); + } + + public override void Close() + { + Debug.Console(1, this, "Closing Shade: '{0}'", this.Name); + + PulseOutput(CloseRelay, RelayPulseTime); + } + + void PulseOutput(ISwitchedOutput output, int pulseTime) + { + output.On(); + CTimer pulseTimer = new CTimer(new CTimerCallbackFunction((o) => output.Off()), pulseTime); + } + + /// + /// Attempts to get the port on teh specified device from config + /// + /// + /// + ISwitchedOutput GetSwitchedOutputFromDevice(IOPortConfig relayConfig) + { + var portDevice = DeviceManager.GetDeviceForKey(relayConfig.PortDeviceKey); + + if (portDevice != null) + { + return (portDevice as ISwitchedOutputCollection).SwitchedOutputs[relayConfig.PortNumber]; + } + else + { + Debug.Console(1, this, "Error: Unable to get relay on port '{0}' from device with key '{1}'", relayConfig.PortNumber, relayConfig.PortDeviceKey); + return null; + } + } + + } + + public class RelayControlledShadeConfigProperties + { + public int RelayPulseTime { get; set; } + public ShadeRelaysConfig Relays { get; set; } + public string StopOrPresetLabel { get; set; } + + public class ShadeRelaysConfig + { + public IOPortConfig Open { get; set; } + public IOPortConfig StopOrPreset { get; set; } + public IOPortConfig Close { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj new file mode 100644 index 00000000..cf723ecf --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj @@ -0,0 +1,205 @@ + + + Release + AnyCPU + 9.0.30729 + 2.0 + {892B761C-E479-44CE-BD74-243E9214AF13} + Library + Properties + PepperDash.Essentials.Devices.Common + Essentials Devices Common + {0B4745B0-194B-4BB6-8E21-E9057CA92300};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + WindowsCE + E2BECB1F-8C8C-41ba-B736-9BE7D946A398 + 5.0 + SmartDeviceProject1 + v3.5 + Windows CE + + + + + .allowedReferenceRelatedFileExtensions + true + full + false + bin\ + DEBUG;TRACE; + prompt + 4 + 512 + true + true + off + + + .allowedReferenceRelatedFileExtensions + none + true + bin\ + prompt + 4 + 512 + true + true + off + + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Gateways.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.GeneralIO.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Lighting.dll + + + + False + ..\..\references\PepperDash_Core.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + False + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + False + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + False + + + False + ..\..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5} + PepperDash_Essentials_Core + + + + + + + + + rem S# Pro preparation will execute after these operations + + \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertsEndpointStatusServer.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertsEndpointStatusServer.cs new file mode 100644 index 00000000..5867b51a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertsEndpointStatusServer.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Text.RegularExpressions; +using Crestron.SimplSharp.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +using Crestron.SimplSharp.CrestronSockets; + + +namespace PepperDash.Essentials.Devices.Common +{ + + /***** + * TODO JTA: Add Polling + * TODO JTA: Move all the registration commnads to the EvertEndpoint class. + * + * + * + * + * + */ + + public class EvertzEndpointStatusServer : Device + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + public bool isSubscribed; + public string Address; + public GenericUdpServer Server; + + + + /// + /// Shows received lines as hex + /// + public bool ShowHexResponse { get; set; } + public Dictionary Endpoints; + public Dictionary ServerIdByEndpointIp; + + public EvertzEndpointStatusServer(string key, string name, GenericUdpServer server, EvertzEndpointStatusServerPropertiesConfig props) : + base(key, name) + { + Server = server; + Address = props.serverHostname; + Server.DataRecievedExtra += new EventHandler(_Server_DataRecievedExtra); + + Server.Connect(); + Endpoints = new Dictionary(); + + //CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); + } + + + + //TODO JTA: Move this method and process over to the endpoint itself. return a bool. + public bool RegisterEvertzEndpoint (EvertzEndpoint device) + { + + if (Endpoints.ContainsKey(device.Address) == false) + { + Endpoints.Add(device.Address, device); + } + + return true; + } + + + + + void _Server_DataRecievedExtra(object sender, GenericUdpReceiveTextExtraArgs e) + { + Debug.Console(2, this, "_Server_DataRecievedExtra:\nIP:{0}\nPort:{1}\nText{2}\nBytes:{3} ", e.IpAddress, e.Port, e.Text, e.Bytes); + } + + public override bool CustomActivate() + { + /* + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + */ + + + + + + return true; + } + + } + + + + + + + + + + + + public class EvertzPortRestResponse + { + public string id; + public string name; + public string type; + public string value; + + } + + public class EvertsStatusRequesstResponse + { + public EvertzStatusDataResponse data; + public string error; + } + + public class EvertzStatusDataResponse + { + public List servers; + } + + public class EvertzServerStatusResponse + { + public string id; + public string name; + public EvertsServerStausNotificationsResponse notify; + + } + public class EvertsServerStausNotificationsResponse + { + public string ip; + public List parameters; + public string port; + public string protocol; + } + public class EvertzEndpointStatusServerPropertiesConfig + { + + public ControlPropertiesConfig control { get; set; } + public string serverHostname { get; set; } + + } + + } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertzEndpoint.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertzEndpoint.cs new file mode 100644 index 00000000..7ab504d1 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertzEndpoint.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Text.RegularExpressions; +using Crestron.SimplSharp.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + + +namespace PepperDash.Essentials.Devices.Common +{ + + public class EvertzEndpoint : Device + { + + + + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public GenericCommunicationMonitor CommunicationMonitor { get; private set; } + + private GenericHttpClient Client; + public string userName; + public string password; + public string Address; + private bool OnlineStatus; + public BoolFeedback OnlineFeedback; + public IntFeedback PresetFeedback; + + + public bool isSubscribed; + + + + CrestronQueue CommandQueue; + + public Dictionary Ports; + + private string _ControllerKey; + + + private EvertzEndpointStatusServer StatusServer; + private String StatusServerId; + + /// + /// Shows received lines as hex + /// + public bool ShowHexResponse { get; set; } + + public EvertzEndpoint(string key, string name, EvertzEndpointPropertiesConfig props, string type) : + base(key, name) + { + + + this.Address = props.address; + Client = new GenericHttpClient(string.Format("{0}-GenericWebClient", name), string.Format("{0}-GenericWebClient", name), this.Address); + Client.ResponseRecived += new EventHandler(Client_ResponseRecived); + Ports = new Dictionary(); + if (type.ToLower() == "mma10g-trs4k") + { + //create port hdmi 01 + EvertzEndpointPort hdmi1 = new EvertzEndpointPort("HDMI01", "131.0@s", "136.0@s"); + EvertzEndpointPort hdmi2 = new EvertzEndpointPort("HDMI02", "131.1@s", "136.1@s"); + // add to dictionay with all keys + addPortToDictionary(hdmi1); + addPortToDictionary(hdmi2); + } + _ControllerKey = null; + if (props.controllerKey != null) + { + _ControllerKey = props.controllerKey; + } + AddPostActivationAction( () => {PostActivation();}); + CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Client, props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Client, 40000, 120000, 300000, "v.api/apis/EV/SERVERSTATUS"); + } + + + } + + /// + /// Helper method + /// + /// + private void addPortToDictionary(EvertzEndpointPort port) + { + Ports.Add(port.PortName, port); + Ports.Add(port.ResolutionVarID, port); + Ports.Add(port.SyncVarID, port); + //PollForState(port.SyncVarID); + //PollForState(port.ResolutionVarID); + } + + /// + /// + /// + /// + public override bool CustomActivate() + { + + // Create Device -> Constructor fires + // PreActivations get called + // CustomActivate Gets Called Anything that is involved with this single class Ex: Connection, Setup Feedback, Etc. + // After this point all devices are ready for interaction + // PostActivation gets called. Use this for interClass activation. + CommunicationMonitor.Start(); + OnlineFeedback = new BoolFeedback(() => { return OnlineStatus; }); + + + //CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + return true; + } + + /// + /// + /// + private void PostActivation() + { + Debug.Console(2, this, "EvertzEndpoint Post Activation"); + if (_ControllerKey != null) + { + StatusServer = DeviceManager.GetDeviceForKey(_ControllerKey) as EvertzEndpointStatusServer; + StatusServer.RegisterEvertzEndpoint(this); + + // RegisterStatusServer(); + // SendStatusRequest(); + } + // PollAll(); + } + + void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType == eProgramStatusEventType.Stopping) + { + Debug.Console(1, this, "Program stopping. Disabling EvertzStatusServer"); + if (StatusServerId != null) + { + //UnregisterServer(); + } + } + } + + private void ProcessServerStatusRequest(EvertsStatusRequesstResponse status) + { + // var status = JsonConvert.DeserializeObject(SendStatusRequest()); + if (status.error != null) + { + + } + else if (status.data != null) + { + foreach (var server in status.data.servers) + { + if (server.name == string.Format("{0}-{1}", this.Name, StatusServer.Address)) + { + StatusServerId = server.id; + Debug.Console(2, this, "EvertzEndpoint {0} StatusServer {1} Registered ID {2}", Name, StatusServer.Name, StatusServerId); + /* + + foreach (var port in Ports) + { + // TODO JTA: This needs a better check + // you get a {"status": "success"} or "error": "error to register notification- Variable exists.." + if (!server.notify.parameters.Contains(port.Value.ResolutionVarID)) + { + RegisterForNotification(StatusServerId, port.Value.ResolutionVarID); + } + if (!server.notify.parameters.Contains(port.Value.ResolutionVarID)) + { + RegisterForNotification(StatusServerId, port.Value.SyncVarID); + } + } + */ + + break; + } + } + StatusServerId = null; + } + } + private void RegisterServerWithEndpoint() + { + /* + var registrationResult = RegisterServer(StatusServer.Address, string.Format("{0}-{1}", this.Name, StatusServer.Address), StatusServer.Server.Port.ToString()); + Debug.Console(2, this, "EvertzEndpointStatusServer Registration Result with device {0}\n{1}", Address, registrationResult); + if (registrationResult.Contains("success")) + { + RegisterStatusServer(); + } + else + { + Debug.Console(0, this, "EvertzEndpointStatusServer RegisterServerWithEndpoint with device {0}\n{1}", Address, registrationResult); + + } + * */ + } + + public void PollAll() + { + string collection = ""; + foreach (var parameter in Ports) + { + if (parameter.Key.Contains("@")) + { + collection = collection + parameter.Key + ","; + } + } + collection = collection.Substring(0, collection.Length - 1); + SendGetRequest(collection); + } + public void PollForState(string varId) + { + try + { + SendGetRequest(varId); + //var returnState = JsonConvert.DeserializeObject(SendGetRequest(varId)); + + } + catch (Exception e) + { + Debug.Console(0, this, "PollForState {0}", e); + + } + } + + + public void ProcessGetParameterResponse(EvertzPortRestResponse response) + { + var PortObject = Ports[response.id]; + if (response.name == "Input Status") + { + if (response.value == "Missing") { PortObject.SyncDetected = false; } + else { PortObject.SyncDetected = true; } + } + } + public void SendGetRequest(string s) + { + Client.SendText("v.api/apis/EV/GET/parameter/{0}", s); + } + + public void SendStatusRequest() + { + Client.SendText("/v.api/apis/EV/SERVERSTATUS"); + } + public void RegisterServer(string hostname, string servername, string port) + { + Client.SendText("v.api/apis/EV/SERVERADD/server/{0}/{1}/{2}/udp", hostname, servername, port); + } + public void UnregisterServer() + { + if (StatusServerId != null) + { + Client.SendTextNoResponse("v.api/apis/EV/SERVERDEL/server/{0}", StatusServerId); + } + } + + // TODO JTA: Craete a UnregisterServerFast using DispatchASync. + public void RegisterForNotification(string varId) + { + Client.SendText("v.api/apis/EV/NOTIFYADD/parameter/{0}/{1}", StatusServerId, varId); + } + + + void Client_ResponseRecived(object sender, GenericHttpClientEventArgs e) + { + if (e.Error == HTTP_CALLBACK_ERROR.COMPLETED) + { + if (e.RequestPath.Contains("GET/parameter/")) + { + // Get Parameter response + if (!e.ResponseText.Contains("[")) + ProcessGetParameterResponse(JsonConvert.DeserializeObject(e.ResponseText)); + else if (e.ResponseText.Contains("[")) + { + List test = JsonConvert.DeserializeObject>(e.ResponseText); + foreach (var thing in test) + { + ProcessGetParameterResponse(thing); + } + + } + } + else if (e.RequestPath.Contains("SERVERSTATUS")) + { + PollAll(); + ProcessServerStatusRequest(JsonConvert.DeserializeObject(e.ResponseText)); + } + } + } + + + + + public class EvertzPortsRestResponse + { + List test; + } + public class EvertzPortRestResponse + { + public string id; + public string name; + public string type; + public string value; + + } + + public class EvertzEndpointPort + { + public string PortName; + public string SyncVarID; + public string ResolutionVarID; + public bool SyncDetected; + public string Resolution; + public BoolFeedback SyncDetectedFeedback; + + public EvertzEndpointPort (string portName, string syncVarId, string resolutionVarId) + { + PortName = portName; + SyncVarID = syncVarId; + ResolutionVarID = resolutionVarId; + SyncDetectedFeedback = new BoolFeedback(() => { return SyncDetected; }); + } + + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertzEndpointPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertzEndpointPropertiesConfig.cs new file mode 100644 index 00000000..5beff4df --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertzEndpointPropertiesConfig.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common +{ + /// + /// + /// + public class EvertzEndpointPropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public ControlPropertiesConfig Control { get; set; } + public string userName { get; set; } + public string password { get; set; } + public string address { get; set; } + public string controllerKey { get; set; } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertzEndpointVarIds.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertzEndpointVarIds.cs new file mode 100644 index 00000000..fc5200f1 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/EvertzEndpointVarIds.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common +{ + public class EvertzEndpointVarIds + { + private string HdmiPort01SyncStatus = "136.0"; + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/GenericHttpClient.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/GenericHttpClient.cs new file mode 100644 index 00000000..53b4efd6 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Evertz/GenericHttpClient.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Net.Http; +using PepperDash.Core; +using PepperDash.Core.DebugThings; + +namespace PepperDash.Essentials.Devices.Common +{ + public class GenericHttpClient : Device, IBasicCommunication + { + public HttpClient Client; + public event EventHandler ResponseRecived; + + public GenericHttpClient(string key, string name, string hostname) + : base(key, name) + { + Client = new HttpClient(); + Client.HostName = hostname; + + + } + + + /// + /// + /// + /// + public void SendText(string path) + { + HttpClientRequest request = new HttpClientRequest(); + string url = string.Format("http://{0}/{1}", Client.HostName, path); + request.Url = new UrlParser(url); + HttpClient.DISPATCHASYNC_ERROR error = Client.DispatchAsyncEx(request, Response, request); + Debug.Console(2, this, "GenericHttpClient SentRequest TX:'{0}'", url); + } + public void SendText(string format, params object[] items) + { + HttpClientRequest request = new HttpClientRequest(); + string url = string.Format("http://{0}/{1}", Client.HostName, string.Format(format, items)); + request.Url = new UrlParser(url); + HttpClient.DISPATCHASYNC_ERROR error = Client.DispatchAsyncEx(request, Response, request); + Debug.Console(2, this, "GenericHttpClient SentRequest TX:'{0}'", url); + } + + public void SendTextNoResponse(string format, params object[] items) + { + HttpClientRequest request = new HttpClientRequest(); + string url = string.Format("http://{0}/{1}", Client.HostName, string.Format(format, items)); + request.Url = new UrlParser(url); + Client.Dispatch(request); + Debug.Console(2, this, "GenericHttpClient SentRequest TX:'{0}'", url); + } + private void Response(HttpClientResponse response, HTTP_CALLBACK_ERROR error, object request) + { + if (error == HTTP_CALLBACK_ERROR.COMPLETED) + { + var responseReceived = response; + + if (responseReceived.ContentString.Length > 0) + { + if (ResponseRecived != null) + ResponseRecived(this, new GenericHttpClientEventArgs(responseReceived.ContentString, (request as HttpClientRequest).Url.ToString(), error)); + + Debug.Console(2, this, "GenericHttpClient ResponseReceived"); + Debug.Console(2, this, "RX:{0}", responseReceived.ContentString); + Debug.Console(2, this, "TX:{0}", (request as HttpClientRequest).Url.ToString()); + } + } + + } + + + #region IBasicCommunication Members + + public void SendBytes(byte[] bytes) + { + throw new NotImplementedException(); + } + + + + #endregion + + #region ICommunicationReceiver Members + + public event EventHandler BytesReceived; + + public void Connect() + { + throw new NotImplementedException(); + } + + public void Disconnect() + { + throw new NotImplementedException(); + } + + public bool IsConnected + { + get { return true; } + } + + public event EventHandler TextReceived; + + #endregion + } + public class GenericHttpClientEventArgs : EventArgs + { + public string ResponseText { get; private set; } + public string RequestPath { get; private set; } + public HTTP_CALLBACK_ERROR Error { get; set; } + public GenericHttpClientEventArgs(string response, string request, HTTP_CALLBACK_ERROR error) + { + ResponseText = response; + RequestPath = request; + Error = error; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs new file mode 100644 index 00000000..9c581c40 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs @@ -0,0 +1,407 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.GeneralIO; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.CrestronIO; + +using PepperDash.Essentials.Devices.Common; +using PepperDash.Essentials.Devices.Common.DSP; +using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Devices.Common.Occupancy; +using PepperDash.Essentials.Devices.Common.Environment; + + + +namespace PepperDash.Essentials.Devices.Common +{ + public class DeviceFactory + { + public static IKeyed GetDevice(DeviceConfig dc) + { + var key = dc.Key; + var name = dc.Name; + var type = dc.Type; + var properties = dc.Properties; + var propAnon = new {}; + JsonConvert.DeserializeAnonymousType(dc.Properties.ToString(), propAnon); + + var typeName = dc.Type.ToLower(); + var groupName = dc.Group.ToLower(); + + if (typeName == "appletv") + { + var irCont = IRPortHelper.GetIrOutputPortController(dc); + return new AppleTV(key, name, irCont); + } + else if (typeName == "analogwaylivecore") + { + var comm = CommFactory.CreateCommForDevice(dc); + var props = JsonConvert.DeserializeObject( + properties.ToString()); + return new AnalogWayLiveCore(key, name, comm, props); + } + else if (typeName == "basicirdisplay") + { + var ir = IRPortHelper.GetIrPort(properties); + if (ir != null) + { + var display = new BasicIrDisplay(key, name, ir.Port, ir.FileName); + display.IrPulseTime = 200; // Set default pulse time for IR commands. + return display; + } + } + + else if (typeName == "biamptesira") + { + var comm = CommFactory.CreateCommForDevice(dc); + var props = JsonConvert.DeserializeObject( + properties.ToString()); + return new BiampTesiraForteDsp(key, name, comm, props); + } + + + else if (typeName == "cameravisca") + { + var comm = CommFactory.CreateCommForDevice(dc); + var props = JsonConvert.DeserializeObject( + properties.ToString()); + return new Cameras.CameraVisca(key, name, comm, props); + } + + else if (typeName == "cenrfgwex") + { + return CenRfgwController.GetNewExGatewayController(key, name, + properties.Value("id"), properties.Value("gatewayType")); + } + + else if (typeName == "cenerfgwpoe") + { + return CenRfgwController.GetNewErGatewayController(key, name, + properties.Value("id"), properties.Value("gatewayType")); + } + + else if (groupName == "discplayer") // (typeName == "irbluray") + { + if (properties["control"]["method"].Value() == "ir") + { + var irCont = IRPortHelper.GetIrOutputPortController(dc); + return new IRBlurayBase(key, name, irCont); + } + else if (properties["control"]["method"].Value() == "com") + { + Debug.Console(0, "[{0}] COM Device type not implemented YET!", key); + } + } + else if (typeName == "digitallogger") + { + // var comm = CommFactory.CreateCommForDevice(dc); + var props = JsonConvert.DeserializeObject( + properties.ToString()); + return new DigitalLogger(key, name, props); + } + else if (groupName == "evertzendpoint") + { + // var comm = CommFactory.CreateCommForDevice(dc); + var props = JsonConvert.DeserializeObject( + properties.ToString()); + return new EvertzEndpoint(key, name, props, typeName); + } + else if (typeName == "evertzendpointstatusserver") + { + var server = CommFactory.CreateCommForDevice(dc) as GenericUdpServer; + + var props = JsonConvert.DeserializeObject( + properties.ToString()); + return new EvertzEndpointStatusServer(key, name, server, props); + } + else if (typeName == "genericaudiooutwithvolume") + { + var zone = dc.Properties.Value("zone"); + return new GenericAudioOutWithVolume(key, name, + dc.Properties.Value("volumeDeviceKey"), zone); + } + + else if (groupName == "genericsource") + { + return new GenericSource(key, name); + } + + else if (typeName == "inroompc") + { + return new InRoomPc(key, name); + } + + else if (typeName == "laptop") + { + return new Laptop(key, name); + } + + else if (typeName == "mockvc") + { + return new VideoCodec.MockVC(dc); + } + + else if (typeName == "mockac") + { + var props = JsonConvert.DeserializeObject(properties.ToString()); + return new AudioCodec.MockAC(key, name, props); + } + + else if (typeName.StartsWith("ciscospark")) + { + var comm = CommFactory.CreateCommForDevice(dc); + return new VideoCodec.Cisco.CiscoSparkCodec(dc, comm); + } + + else if (typeName == "zoomroom") + { + var comm = CommFactory.CreateCommForDevice(dc); + return new VideoCodec.ZoomRoom.ZoomRoom(dc, comm); + } + + else if (typeName == "digitalinput") + { + var props = JsonConvert.DeserializeObject(properties.ToString()); + + IDigitalInputPorts portDevice; + + if (props.PortDeviceKey == "processor") + portDevice = Global.ControlSystem as IDigitalInputPorts; + else + portDevice = DeviceManager.GetDeviceForKey(props.PortDeviceKey) as IDigitalInputPorts; + + if (portDevice == null) + Debug.Console(0, "ERROR: Unable to add digital input device with key '{0}'. Port Device does not support digital inputs", key); + else + { + var cs = (portDevice as CrestronControlSystem); + if (cs == null) + { + Debug.Console(0, "ERROR: Port device for [{0}] is not control system", props.PortDeviceKey); + return null; + } + + if (cs.SupportsVersiport) + { + Debug.Console(1, "Attempting to add Digital Input device to Versiport port '{0}'", props.PortNumber); + + if (props.PortNumber > cs.NumberOfVersiPorts) + { + Debug.Console(0, "WARNING: Cannot add Vesiport {0} on {1}. Out of range", + props.PortNumber, props.PortDeviceKey); + return null; + } + + Versiport vp = cs.VersiPorts[props.PortNumber]; + + if (!vp.Registered) + { + var regSuccess = vp.Register(); + if (regSuccess == eDeviceRegistrationUnRegistrationResponse.Success) + { + Debug.Console(1, "Successfully Created Digital Input Device on Versiport"); + return new GenericVersiportDigitalInputDevice(key, vp, props); + } + else + { + Debug.Console(0, "WARNING: Attempt to register versiport {0} on device with key '{1}' failed: {2}", + props.PortNumber, props.PortDeviceKey, regSuccess); + return null; + } + } + } + else if (cs.SupportsDigitalInput) + { + Debug.Console(1, "Attempting to add Digital Input device to Digital Input port '{0}'", props.PortNumber); + + if (props.PortNumber > cs.NumberOfDigitalInputPorts) + { + Debug.Console(0, "WARNING: Cannot register DIO port {0} on {1}. Out of range", + props.PortNumber, props.PortDeviceKey); + return null; + } + + DigitalInput digitalInput = cs.DigitalInputPorts[props.PortNumber]; + + if (!digitalInput.Registered) + { + if (digitalInput.Register() == eDeviceRegistrationUnRegistrationResponse.Success) + { + Debug.Console(1, "Successfully Created Digital Input Device on Digital Input"); + return new GenericDigitalInputDevice(key, digitalInput); + } + else + Debug.Console(0, "WARNING: Attempt to register digital input {0} on device with key '{1}' failed.", + props.PortNumber, props.PortDeviceKey); + } + } + } + } + + else if (typeName == "relayoutput") + { + var props = JsonConvert.DeserializeObject(properties.ToString()); + + IRelayPorts portDevice; + + if (props.PortDeviceKey == "processor") + portDevice = Global.ControlSystem as IRelayPorts; + else + portDevice = DeviceManager.GetDeviceForKey(props.PortDeviceKey) as IRelayPorts; + + if (portDevice == null) + Debug.Console(0, "Unable to add relay device with key '{0}'. Port Device does not support relays", key); + else + { + var cs = (portDevice as CrestronControlSystem); + + if (cs != null) + { + // The relay is on a control system processor + if (!cs.SupportsRelay || props.PortNumber > cs.NumberOfRelayPorts) + { + Debug.Console(0, "Port Device: {0} does not support relays or does not have enough relays"); + return null; + } + } + else + { + // The relay is on another device type + + if (props.PortNumber > portDevice.NumberOfRelayPorts) + { + Debug.Console(0, "Port Device: {0} does not have enough relays"); + return null; + } + } + + Relay relay = portDevice.RelayPorts[props.PortNumber]; + + if (!relay.Registered) + { + if (relay.Register() == eDeviceRegistrationUnRegistrationResponse.Success) + return new GenericRelayDevice(key, relay); + else + Debug.Console(0, "Attempt to register relay {0} on device with key '{1}' failed.", props.PortNumber, props.PortDeviceKey); + } + + // Future: Check if portDevice is 3-series card or other non control system that supports versiports + } + } + + else if (typeName == "microphoneprivacycontroller") + { + var props = JsonConvert.DeserializeObject(properties.ToString()); + + return new Microphones.MicrophonePrivacyController(key, props); + } + else if (typeName == "roku") + { + var irCont = IRPortHelper.GetIrOutputPortController(dc); + return new Roku2(key, name, irCont); + } + + else if (groupName == "settopbox") //(typeName == "irstbbase") + { + var irCont = IRPortHelper.GetIrOutputPortController(dc); + var config = dc.Properties.ToObject(); + var stb = new IRSetTopBoxBase(key, name, irCont, config); + + //stb.HasDvr = properties.Value("hasDvr"); + var listName = properties.Value("presetsList"); + if (listName != null) + stb.LoadPresets(listName); + return stb; + } + else if (typeName == "tvonecorio") + { + var comm = CommFactory.CreateCommForDevice(dc); + var props = JsonConvert.DeserializeObject( + properties.ToString()); + return new TVOneCorio(key, name, comm, props); + } + + + else if (typeName == "glsoirccn") + { + var comm = CommFactory.GetControlPropertiesConfig(dc); + + GlsOccupancySensorBase occSensor = null; + + occSensor = new GlsOirCCn(comm.CresnetIdInt, Global.ControlSystem); + + if (occSensor != null) + return new EssentialsGlsOccupancySensorBaseController(key, name, occSensor); + else + Debug.Console(0, "ERROR: Unable to create Occupancy Sensor Device. Key: '{0}'", key); + } + + else if (typeName == "glsodtccn") + { + var comm = CommFactory.GetControlPropertiesConfig(dc); + + GlsOccupancySensorBase occSensor = null; + + occSensor = new GlsOdtCCn(comm.CresnetIdInt, Global.ControlSystem); + + if (occSensor != null) + return new EssentialsGlsOccupancySensorBaseController(key, name, occSensor); + else + Debug.Console(0, "ERROR: Unable to create Occupancy Sensor Device. Key: '{0}'", key); + } + + else if (groupName == "lighting") + { + if (typeName == "lutronqs") + { + var comm = CommFactory.CreateCommForDevice(dc); + + var props = JsonConvert.DeserializeObject(properties.ToString()); + + return new Environment.Lutron.LutronQuantumArea(key, name, comm, props); + } + else if (typeName == "din8sw8") + { + var comm = CommFactory.GetControlPropertiesConfig(dc); + + return new Environment.Lighting.Din8sw8Controller(key, comm.CresnetIdInt); + } + + } + + else if (groupName == "environment") + { + if (typeName == "shadecontroller") + { + var props = JsonConvert.DeserializeObject(properties.ToString()); + + return new Core.Shades.ShadeController(key, name, props); + } + else if (typeName == "relaycontrolledshade") + { + var props = JsonConvert.DeserializeObject(properties.ToString()); + + return new Environment.Somfy.RelayControlledShade(key, name, props); + } + + } + //else if (typeName == "qscdsp") + //{ + // var comm = CommFactory.CreateCommForDevice(dc); + // var props = JsonConvert.DeserializeObject( + // properties.ToString()); + // return new QscDsp(key, name, comm, props); + //} + + return null; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Generic/GenericSource.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Generic/GenericSource.cs new file mode 100644 index 00000000..04fae6a2 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Generic/GenericSource.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Common +{ + public class GenericSource : Device, IUiDisplayInfo, IRoutingOutputs, IUsageTracking + { + + public uint DisplayUiType { get { return DisplayUiConstants.TypeNoControls; } } + + public GenericSource(string key, string name) + : base(key, name) + { + + AnyOut = new RoutingOutputPort(RoutingPortNames.AnyOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + OutputPorts = new RoutingPortCollection { AnyOut }; + } + + #region IRoutingOutputs Members + + public RoutingOutputPort AnyOut { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + #region IUsageTracking Members + + public UsageTracking UsageTracker { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/AnalogWay/AnalogWayLiveCorePropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/AnalogWay/AnalogWayLiveCorePropertiesConfig.cs new file mode 100644 index 00000000..a7055310 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/AnalogWay/AnalogWayLiveCorePropertiesConfig.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common +{ + /// + /// + /// + public class AnalogWayLiveCorePropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public ControlPropertiesConfig Control { get; set; } + public string userName { get; set; } + public string password { get; set; } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/AnalogWay/AnalongWayLiveCore.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/AnalogWay/AnalongWayLiveCore.cs new file mode 100644 index 00000000..c2178bdb --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/AnalogWay/AnalongWayLiveCore.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Text.RegularExpressions; + + +namespace PepperDash.Essentials.Devices.Common +{ + + public class AnalogWayLiveCore : Device + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + public string userName; + public string password; + private bool OnlineStatus; + public BoolFeedback OnlineFeedback; + private ushort CurrentPreset; + public IntFeedback PresetFeedback; + + // new public Dictionary LevelControlPoints { get; private set; } + // public List PresetList = new List(); + + public bool isSubscribed; + + private CTimer SubscriptionTimer; + + CrestronQueue CommandQueue; + + bool CommandQueueInProgress = false; + + //new public Dictionary DialerControlPoints { get; private set; } + + //new public Dictionary SwitcherControlPoints { get; private set; } + + /// + /// Shows received lines as hex + /// + public bool ShowHexResponse { get; set; } + + public AnalogWayLiveCore(string key, string name, IBasicCommunication comm, AnalogWayLiveCorePropertiesConfig props) : + base(key, name) + { + + this.userName = props.userName; + this.password = props.password; + CommandQueue = new CrestronQueue(100); + + + Communication = comm; + + var socket = comm as ISocketStatus; + if (socket != null) + { + // This instance uses IP control + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + else + { + // This instance uses RS-232 control + } + PortGather = new CommunicationGather(Communication, "\x0a"); + PortGather.LineReceived += this.Port_LineReceived; + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + //#warning Need to deal with this poll string + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 120000, 120000, 300000, "System.Status\x0A\x0D"); + } + + } + + public override bool CustomActivate() + { + + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + + OnlineFeedback = new BoolFeedback(() => { return OnlineStatus; }); + PresetFeedback = new IntFeedback(() => { return CurrentPreset; }); + + CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + return true; + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString()); + + if (e.Client.IsConnected) + { + OnlineStatus = true; + OnlineFeedback.FireUpdate(); + } + else + { + OnlineStatus = false; + OnlineFeedback.FireUpdate(); + if (SubscriptionTimer != null) + { + SubscriptionTimer.Stop(); + SubscriptionTimer = null; + } + + isSubscribed = false; + CommandQueue.Clear(); + CommandQueueInProgress = false; + } + } + + /// + /// Initiates the subscription process to the DSP + /// + + + + + /// + /// Handles a response message from the DSP + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + Debug.Console(2, this, "TVOneCurio RX: '{0}'", args.Text); + try + { + if (args.Text.IndexOf("login") > -1) + { + SendLine(string.Format("Login({0},{1})", this.userName, this.password)); + } + else if (args.Text.IndexOf("!Done Preset.Take =") > -1) + { + + string presetNumberParse = args.Text.Remove(0, args.Text.IndexOf("=") + 2); + + Debug.Console(1, this, "Preset Parse: {0}", presetNumberParse); + CurrentPreset = ushort.Parse(presetNumberParse); + PresetFeedback.FireUpdate(); + + + } + + + } + catch (Exception e) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Error parsing response: '{0}'\n{1}", args.Text, e); + } + + } + + /// + /// Sends a command to the DSP (with delimiter appended) + /// + /// Command to send + public void SendLine(string s) + { + Debug.Console(1, this, "TVOne Cusio TX: '{0}'", s); + Communication.SendText(s + "\x0d\x0a"); + } + + /// + /// Adds a command from a child module to the queue + /// + /// Command object from child module + public void EnqueueCommand(QueuedCommand commandToEnqueue) + { + CommandQueue.Enqueue(commandToEnqueue); + //Debug.Console(1, this, "Command (QueuedCommand) Enqueued '{0}'. CommandQueue has '{1}' Elements.", commandToEnqueue.Command, CommandQueue.Count); + + if(!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Adds a raw string command to the queue + /// + /// + public void EnqueueCommand(string command) + { + CommandQueue.Enqueue(command); + //Debug.Console(1, this, "Command (string) Enqueued '{0}'. CommandQueue has '{1}' Elements.", command, CommandQueue.Count); + + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Sends the next queued command to the DSP + /// + void SendNextQueuedCommand() + { + if (Communication.IsConnected && !CommandQueue.IsEmpty) + { + CommandQueueInProgress = true; + + if (CommandQueue.Peek() is QueuedCommand) + { + QueuedCommand nextCommand = new QueuedCommand(); + + nextCommand = (QueuedCommand)CommandQueue.Peek(); + + SendLine(nextCommand.Command); + } + else + { + string nextCommand = (string)CommandQueue.Peek(); + + SendLine(nextCommand); + } + } + + } + + + public void CallPreset(ushort presetNumber) + { + SendLine(string.Format("Preset.Take = {0}", presetNumber)); + // SendLine("cgp 1"); + } + + public class QueuedCommand + { + public string Command { get; set; } + public string AttributeCode { get; set; } + // public QscDspControlPoint ControlPoint { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOne/TVOneCorio.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOne/TVOneCorio.cs new file mode 100644 index 00000000..a3dd9745 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOne/TVOneCorio.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Text.RegularExpressions; + + +namespace PepperDash.Essentials.Devices.Common +{ + + public class TVOneCorio : Device + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + public string userName; + public string password; + private bool OnlineStatus; + public BoolFeedback OnlineFeedback; + private ushort CurrentPreset; + public IntFeedback PresetFeedback; + + // new public Dictionary LevelControlPoints { get; private set; } + // public List PresetList = new List(); + + public bool isSubscribed; + + private CTimer SubscriptionTimer; + + CrestronQueue CommandQueue; + + bool CommandQueueInProgress = false; + + //new public Dictionary DialerControlPoints { get; private set; } + + //new public Dictionary SwitcherControlPoints { get; private set; } + + /// + /// Shows received lines as hex + /// + public bool ShowHexResponse { get; set; } + + public TVOneCorio(string key, string name, IBasicCommunication comm, TVOneCorioPropertiesConfig props) : + base(key, name) + { + + this.userName = props.userName; + this.password = props.password; + CommandQueue = new CrestronQueue(100); + + + Communication = comm; + + var socket = comm as ISocketStatus; + if (socket != null) + { + // This instance uses IP control + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + else + { + // This instance uses RS-232 control + } + PortGather = new CommunicationGather(Communication, "\x0a"); + PortGather.LineReceived += this.Port_LineReceived; + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + //#warning Need to deal with this poll string + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 120000, 120000, 300000, "System.Status\x0A\x0D"); + } + + } + + public override bool CustomActivate() + { + + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + + OnlineFeedback = new BoolFeedback(() => { return OnlineStatus; }); + PresetFeedback = new IntFeedback(() => { return CurrentPreset; }); + + CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + return true; + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString()); + + if (e.Client.IsConnected) + { + OnlineStatus = true; + OnlineFeedback.FireUpdate(); + } + else + { + OnlineStatus = false; + OnlineFeedback.FireUpdate(); + if (SubscriptionTimer != null) + { + SubscriptionTimer.Stop(); + SubscriptionTimer = null; + } + + isSubscribed = false; + CommandQueue.Clear(); + CommandQueueInProgress = false; + } + } + + /// + /// Initiates the subscription process to the DSP + /// + + + + + /// + /// Handles a response message from the DSP + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + Debug.Console(2, this, "TVOneCurio RX: '{0}'", args.Text); + try + { + if (args.Text.IndexOf("login") > -1) + { + SendLine(string.Format("Login({0},{1})", this.userName, this.password)); + } + else if (args.Text.IndexOf("!Done Preset.Take =") > -1) + { + + string presetNumberParse = args.Text.Remove(0, args.Text.IndexOf("=") + 2); + + Debug.Console(1, this, "Preset Parse: {0}", presetNumberParse); + CurrentPreset = ushort.Parse(presetNumberParse); + PresetFeedback.FireUpdate(); + + + } + + + } + catch (Exception e) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Error parsing response: '{0}'\n{1}", args.Text, e); + } + + } + + /// + /// Sends a command to the DSP (with delimiter appended) + /// + /// Command to send + public void SendLine(string s) + { + Debug.Console(1, this, "TVOne Cusio TX: '{0}'", s); + Communication.SendText(s + "\x0d\x0a"); + } + + /// + /// Adds a command from a child module to the queue + /// + /// Command object from child module + public void EnqueueCommand(QueuedCommand commandToEnqueue) + { + CommandQueue.Enqueue(commandToEnqueue); + //Debug.Console(1, this, "Command (QueuedCommand) Enqueued '{0}'. CommandQueue has '{1}' Elements.", commandToEnqueue.Command, CommandQueue.Count); + + if(!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Adds a raw string command to the queue + /// + /// + public void EnqueueCommand(string command) + { + CommandQueue.Enqueue(command); + //Debug.Console(1, this, "Command (string) Enqueued '{0}'. CommandQueue has '{1}' Elements.", command, CommandQueue.Count); + + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Sends the next queued command to the DSP + /// + void SendNextQueuedCommand() + { + if (Communication.IsConnected && !CommandQueue.IsEmpty) + { + CommandQueueInProgress = true; + + if (CommandQueue.Peek() is QueuedCommand) + { + QueuedCommand nextCommand = new QueuedCommand(); + + nextCommand = (QueuedCommand)CommandQueue.Peek(); + + SendLine(nextCommand.Command); + } + else + { + string nextCommand = (string)CommandQueue.Peek(); + + SendLine(nextCommand); + } + } + + } + + + public void CallPreset(ushort presetNumber) + { + SendLine(string.Format("Preset.Take = {0}", presetNumber)); + // SendLine("cgp 1"); + } + + public class QueuedCommand + { + public string Command { get; set; } + public string AttributeCode { get; set; } + // public QscDspControlPoint ControlPoint { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOne/TVOneCorioPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOne/TVOneCorioPropertiesConfig.cs new file mode 100644 index 00000000..410fdb27 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOne/TVOneCorioPropertiesConfig.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common +{ + /// + /// + /// + public class TVOneCorioPropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public ControlPropertiesConfig Control { get; set; } + public string userName { get; set; } + public string password { get; set; } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOneCorio.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOneCorio.cs new file mode 100644 index 00000000..a3dd9745 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOneCorio.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Text.RegularExpressions; + + +namespace PepperDash.Essentials.Devices.Common +{ + + public class TVOneCorio : Device + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + public string userName; + public string password; + private bool OnlineStatus; + public BoolFeedback OnlineFeedback; + private ushort CurrentPreset; + public IntFeedback PresetFeedback; + + // new public Dictionary LevelControlPoints { get; private set; } + // public List PresetList = new List(); + + public bool isSubscribed; + + private CTimer SubscriptionTimer; + + CrestronQueue CommandQueue; + + bool CommandQueueInProgress = false; + + //new public Dictionary DialerControlPoints { get; private set; } + + //new public Dictionary SwitcherControlPoints { get; private set; } + + /// + /// Shows received lines as hex + /// + public bool ShowHexResponse { get; set; } + + public TVOneCorio(string key, string name, IBasicCommunication comm, TVOneCorioPropertiesConfig props) : + base(key, name) + { + + this.userName = props.userName; + this.password = props.password; + CommandQueue = new CrestronQueue(100); + + + Communication = comm; + + var socket = comm as ISocketStatus; + if (socket != null) + { + // This instance uses IP control + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + else + { + // This instance uses RS-232 control + } + PortGather = new CommunicationGather(Communication, "\x0a"); + PortGather.LineReceived += this.Port_LineReceived; + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + //#warning Need to deal with this poll string + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 120000, 120000, 300000, "System.Status\x0A\x0D"); + } + + } + + public override bool CustomActivate() + { + + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + + OnlineFeedback = new BoolFeedback(() => { return OnlineStatus; }); + PresetFeedback = new IntFeedback(() => { return CurrentPreset; }); + + CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + return true; + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString()); + + if (e.Client.IsConnected) + { + OnlineStatus = true; + OnlineFeedback.FireUpdate(); + } + else + { + OnlineStatus = false; + OnlineFeedback.FireUpdate(); + if (SubscriptionTimer != null) + { + SubscriptionTimer.Stop(); + SubscriptionTimer = null; + } + + isSubscribed = false; + CommandQueue.Clear(); + CommandQueueInProgress = false; + } + } + + /// + /// Initiates the subscription process to the DSP + /// + + + + + /// + /// Handles a response message from the DSP + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + Debug.Console(2, this, "TVOneCurio RX: '{0}'", args.Text); + try + { + if (args.Text.IndexOf("login") > -1) + { + SendLine(string.Format("Login({0},{1})", this.userName, this.password)); + } + else if (args.Text.IndexOf("!Done Preset.Take =") > -1) + { + + string presetNumberParse = args.Text.Remove(0, args.Text.IndexOf("=") + 2); + + Debug.Console(1, this, "Preset Parse: {0}", presetNumberParse); + CurrentPreset = ushort.Parse(presetNumberParse); + PresetFeedback.FireUpdate(); + + + } + + + } + catch (Exception e) + { + if (Debug.Level == 2) + Debug.Console(2, this, "Error parsing response: '{0}'\n{1}", args.Text, e); + } + + } + + /// + /// Sends a command to the DSP (with delimiter appended) + /// + /// Command to send + public void SendLine(string s) + { + Debug.Console(1, this, "TVOne Cusio TX: '{0}'", s); + Communication.SendText(s + "\x0d\x0a"); + } + + /// + /// Adds a command from a child module to the queue + /// + /// Command object from child module + public void EnqueueCommand(QueuedCommand commandToEnqueue) + { + CommandQueue.Enqueue(commandToEnqueue); + //Debug.Console(1, this, "Command (QueuedCommand) Enqueued '{0}'. CommandQueue has '{1}' Elements.", commandToEnqueue.Command, CommandQueue.Count); + + if(!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Adds a raw string command to the queue + /// + /// + public void EnqueueCommand(string command) + { + CommandQueue.Enqueue(command); + //Debug.Console(1, this, "Command (string) Enqueued '{0}'. CommandQueue has '{1}' Elements.", command, CommandQueue.Count); + + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Sends the next queued command to the DSP + /// + void SendNextQueuedCommand() + { + if (Communication.IsConnected && !CommandQueue.IsEmpty) + { + CommandQueueInProgress = true; + + if (CommandQueue.Peek() is QueuedCommand) + { + QueuedCommand nextCommand = new QueuedCommand(); + + nextCommand = (QueuedCommand)CommandQueue.Peek(); + + SendLine(nextCommand.Command); + } + else + { + string nextCommand = (string)CommandQueue.Peek(); + + SendLine(nextCommand); + } + } + + } + + + public void CallPreset(ushort presetNumber) + { + SendLine(string.Format("Preset.Take = {0}", presetNumber)); + // SendLine("cgp 1"); + } + + public class QueuedCommand + { + public string Command { get; set; } + public string AttributeCode { get; set; } + // public QscDspControlPoint ControlPoint { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOneCorioPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOneCorioPropertiesConfig.cs new file mode 100644 index 00000000..410fdb27 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/ImageProcessors/TVOneCorioPropertiesConfig.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common +{ + /// + /// + /// + public class TVOneCorioPropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public ControlPropertiesConfig Control { get; set; } + public string userName { get; set; } + public string password { get; set; } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyController.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyController.cs new file mode 100644 index 00000000..177fffd1 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyController.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.CrestronIO; + + +namespace PepperDash.Essentials.Devices.Common.Microphones +{ + /// + /// Used for applications where one or more microphones with momentary contact closure outputs are used to + /// toggle the privacy state of the room. Privacy state feedback is represented + /// + public class MicrophonePrivacyController : Device + { + MicrophonePrivacyControllerConfig Config; + + bool initialized; + + public bool EnableLeds + { + get + { + return _enableLeds; + } + set + { + _enableLeds = value; + + if (initialized) + { + if (value) + { + CheckPrivacyMode(); + SetLedStates(); + } + else + TurnOffAllLeds(); + } + } + } + bool _enableLeds; + + public List Inputs { get; private set; } + + public GenericRelayDevice RedLedRelay { get; private set; } + bool _redLedRelayState; + + public GenericRelayDevice GreenLedRelay { get; private set; } + bool _greenLedRelayState; + + public IPrivacy PrivacyDevice { get; private set; } + + public MicrophonePrivacyController(string key, MicrophonePrivacyControllerConfig config) : + base(key) + { + Config = config; + + Inputs = new List(); + } + + public override bool CustomActivate() + { + foreach (var i in Config.Inputs) + { + var input = DeviceManager.GetDeviceForKey(i.DeviceKey) as IDigitalInput; + + if(input != null) + AddInput(input); + } + + var greenLed = DeviceManager.GetDeviceForKey(Config.GreenLedRelay.DeviceKey) as GenericRelayDevice; + + if (greenLed != null) + GreenLedRelay = greenLed; + else + Debug.Console(0, this, "Unable to add Green LED device"); + + var redLed = DeviceManager.GetDeviceForKey(Config.RedLedRelay.DeviceKey) as GenericRelayDevice; + + if (redLed != null) + RedLedRelay = redLed; + else + Debug.Console(0, this, "Unable to add Red LED device"); + + CheckPrivacyMode(); + + initialized = true; + + return base.CustomActivate(); + } + + public void SetPrivacyDevice(IPrivacy privacyDevice) + { + PrivacyDevice = privacyDevice; + + PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange; + } + + void PrivacyModeIsOnFeedback_OutputChange(object sender, EventArgs e) + { + Debug.Console(1, this, "Privacy mode change: {0}", sender as BoolFeedback); + CheckPrivacyMode(); + } + + void CheckPrivacyMode() + { + if (PrivacyDevice != null) + { + var privacyState = PrivacyDevice.PrivacyModeIsOnFeedback.BoolValue; + + if (privacyState) + TurnOnRedLeds(); + else + TurnOnGreenLeds(); + } + } + + void AddInput(IDigitalInput input) + { + Inputs.Add(input); + + input.InputStateFeedback.OutputChange += InputStateFeedback_OutputChange; + } + + void RemoveInput(IDigitalInput input) + { + var tempInput = Inputs.FirstOrDefault(i => i.Equals(input)); + + if (tempInput != null) + tempInput.InputStateFeedback.OutputChange -= InputStateFeedback_OutputChange; + + Inputs.Remove(input); + } + + void SetRedLedRelay(GenericRelayDevice relay) + { + RedLedRelay = relay; + } + + void SetGreenLedRelay(GenericRelayDevice relay) + { + GreenLedRelay = relay; + } + + /// + /// Check the state of the input change and handle accordingly + /// + /// + /// + void InputStateFeedback_OutputChange(object sender, EventArgs e) + { + if ((sender as BoolFeedback).BoolValue == true) + TogglePrivacyMute(); + } + + /// + /// Toggles the state of the privacy mute + /// + public void TogglePrivacyMute() + { + PrivacyDevice.PrivacyModeToggle(); + } + + void TurnOnRedLeds() + { + _greenLedRelayState = false; + _redLedRelayState = true; + SetLedStates(); + } + + void TurnOnGreenLeds() + { + _redLedRelayState = false; + _greenLedRelayState = true; + SetLedStates(); + } + + /// + /// If enabled, sets the actual state of the relays + /// + void SetLedStates() + { + if (_enableLeds) + { + SetRelayStates(); + } + else + TurnOffAllLeds(); + } + + /// + /// Turns off all LEDs + /// + void TurnOffAllLeds() + { + _redLedRelayState = false; + _greenLedRelayState = false; + + SetRelayStates(); + } + + void SetRelayStates() + { + if (RedLedRelay != null) + { + if (_redLedRelayState) + RedLedRelay.CloseRelay(); + else + RedLedRelay.OpenRelay(); + } + + if(GreenLedRelay != null) + { + if (_greenLedRelayState) + GreenLedRelay.CloseRelay(); + else + GreenLedRelay.OpenRelay(); + } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyControllerConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyControllerConfig.cs new file mode 100644 index 00000000..238fc7cf --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/MIcrophone/MicrophonePrivacyControllerConfig.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core.CrestronIO; + +namespace PepperDash.Essentials.Devices.Common.Microphones +{ + public class MicrophonePrivacyControllerConfig + { + public List Inputs { get; set; } + public KeyedDevice GreenLedRelay { get; set; } + public KeyedDevice RedLedRelay { get; set; } + } + + public class KeyedDevice + { + public string DeviceKey { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/EssentialsGlsOccupancySensorBaseController.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/EssentialsGlsOccupancySensorBaseController.cs new file mode 100644 index 00000000..14892b9a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/EssentialsGlsOccupancySensorBaseController.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.GeneralIO; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Occupancy +{ + public class EssentialsGlsOccupancySensorBaseController : CrestronGenericBaseDevice, IOccupancyStatusProvider + { + public GlsOccupancySensorBase OccSensor { get; private set; } + + public BoolFeedback RoomIsOccupiedFeedback { get; private set; } + + // Debug properties + public bool InTestMode { get; private set; } + + public bool TestRoomIsOccupiedFeedback { get; private set; } + + public Func RoomIsOccupiedFeedbackFunc + { + get + { + return () => InTestMode ? TestRoomIsOccupiedFeedback : OccSensor.OccupancyDetectedFeedback.BoolValue; + } + } + + public EssentialsGlsOccupancySensorBaseController(string key, string name, GlsOccupancySensorBase sensor) + : base(key, name, sensor) + { + OccSensor = sensor; + + RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); + + OccSensor.BaseEvent += new Crestron.SimplSharpPro.BaseEventHandler(OccSensor_BaseEvent); + } + + void OccSensor_BaseEvent(Crestron.SimplSharpPro.GenericBase device, Crestron.SimplSharpPro.BaseEventArgs args) + { + Debug.Console(2, this, "GlsOccupancySensorChange EventId: {0}", args.EventId); + + if (args.EventId == Crestron.SimplSharpPro.GeneralIO.GlsOccupancySensorBase.RoomOccupiedFeedbackEventId + || args.EventId == Crestron.SimplSharpPro.GeneralIO.GlsOccupancySensorBase.RoomVacantFeedbackEventId) + { + Debug.Console(1, this, "Occupancy State: {0}", OccSensor.OccupancyDetectedFeedback.BoolValue); + RoomIsOccupiedFeedback.FireUpdate(); + } + } + + public void SetTestMode(bool mode) + { + InTestMode = mode; + + Debug.Console(1, this, "In Mock Mode: '{0}'", InTestMode); + } + + public void SetTestOccupiedState(bool state) + { + if (!InTestMode) + Debug.Console(1, "Mock mode not enabled"); + else + { + TestRoomIsOccupiedFeedback = state; + + RoomIsOccupiedFeedback.FireUpdate(); + } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/EssentialsGlsOccupancySensorBaseController.cs.orig b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/EssentialsGlsOccupancySensorBaseController.cs.orig new file mode 100644 index 00000000..f65e1b37 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/EssentialsGlsOccupancySensorBaseController.cs.orig @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.GeneralIO; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Occupancy +{ + public class EssentialsGlsOccupancySensorBaseController : CrestronGenericBaseDevice, IOccupancyStatusProvider + { + public GlsOccupancySensorBase OccSensor { get; private set; } + + public BoolFeedback RoomIsOccupiedFeedback { get; private set; } + +<<<<<<< HEAD + /// + /// Set by debugging functions + /// + public bool InMockMode { get; private set; } + + public bool MockRoomIsOccupiedFeedback { get; private set; } +======= + // Debug properties + public bool InTestMode { get; private set; } + + public bool TestRoomIsOccupiedFeedback { get; private set; } +>>>>>>> origin/feature/ecs-342-neil + + public Func RoomIsOccupiedFeedbackFunc + { + get + { +<<<<<<< HEAD + return () => InMockMode ? MockRoomIsOccupiedFeedback : OccSensor.OccupancyDetectedFeedback.BoolValue; +======= + return () => InTestMode ? TestRoomIsOccupiedFeedback : OccSensor.OccupancyDetectedFeedback.BoolValue; +>>>>>>> origin/feature/ecs-342-neil + } + } + + public EssentialsGlsOccupancySensorBaseController(string key, string name, GlsOccupancySensorBase sensor, GlsOccupancySensorConfigurationProperties props) + : base(key, name, sensor) + { + OccSensor = sensor; + RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); + + OccSensor.GlsOccupancySensorChange += new GlsOccupancySensorChangeEventHandler(sensor_GlsOccupancySensorChange); + } + + void sensor_GlsOccupancySensorChange(GlsOccupancySensorBase device, GlsOccupancySensorChangeEventArgs args) + { + RoomIsOccupiedFeedback.FireUpdate(); + } + + public void SetTestMode(bool mode) + { + InTestMode = mode; + + Debug.Console(1, this, "In Mock Mode: '{0}'", InTestMode); + } + + public void SetTestOccupiedState(bool state) + { + if (!InTestMode) + Debug.Console(1, "Mock mode not enabled"); + else + { + TestRoomIsOccupiedFeedback = state; + + RoomIsOccupiedFeedback.FireUpdate(); + } + } + } + + /// + /// + /// + public class GlsOccupancySensorConfigurationProperties + { + public string CresnetId { get; set; } + public string Model { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/EssentialsOccupancyAggregator.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/EssentialsOccupancyAggregator.cs new file mode 100644 index 00000000..e24d468c --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/EssentialsOccupancyAggregator.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Occupancy +{ + /// + /// Aggregates the RoomIsOccupied feedbacks of one or more IOccupancyStatusProvider objects + /// + public class EssentialsOccupancyAggregator : Device, IOccupancyStatusProvider + { + /// + /// Aggregated feedback of all linked IOccupancyStatusProvider devices + /// + public BoolFeedback RoomIsOccupiedFeedback + { + get + { + return AggregatedOccupancyStatus.Output; + } + } + + private BoolFeedbackOr AggregatedOccupancyStatus; + + public EssentialsOccupancyAggregator(string key, string name) + : base(key, name) + { + AggregatedOccupancyStatus = new BoolFeedbackOr(); + } + + /// + /// Adds an IOccupancyStatusProvider device + /// + /// + public void AddOccupancyStatusProvider(IOccupancyStatusProvider statusProvider) + { + AggregatedOccupancyStatus.AddOutputIn(statusProvider.RoomIsOccupiedFeedback); + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/iOccupancyStatusProvider.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/iOccupancyStatusProvider.cs new file mode 100644 index 00000000..02054535 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Occupancy/iOccupancyStatusProvider.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Occupancy +{ + public interface IOccupancyStatusProvider + { + BoolFeedback RoomIsOccupiedFeedback { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/InRoomPc.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/InRoomPc.cs new file mode 100644 index 00000000..0e25ee0d --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/InRoomPc.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharpPro; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Core; + +namespace PepperDash.Essentials.Devices.Common +{ + /// + /// This DVD class should cover most IR, one-way DVD and Bluray fuctions + /// + public class InRoomPc : Device, IHasFeedback, IRoutingOutputs, IAttachVideoStatus, IUiDisplayInfo, IUsageTracking + { + public uint DisplayUiType { get { return DisplayUiConstants.TypeLaptop; } } + public string IconName { get; set; } + public BoolFeedback HasPowerOnFeedback { get; private set; } + + public RoutingOutputPort AnyVideoOut { get; private set; } + + #region IRoutingOutputs Members + + /// + /// Options: hdmi + /// + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + public InRoomPc(string key, string name) + : base(key, name) + { + IconName = "PC"; + HasPowerOnFeedback = new BoolFeedback("HasPowerFeedback", + () => this.GetVideoStatuses() != VideoStatusOutputs.NoStatus); + OutputPorts = new RoutingPortCollection(); + OutputPorts.Add(AnyVideoOut = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.None, 0, this)); + } + + #region IHasFeedback Members + + /// + /// Passes through the VideoStatuses list + /// + public FeedbackCollection Feedbacks + { + get + { + var newList = new FeedbackCollection(); + newList.AddRange(this.GetVideoStatuses().ToList()); + return newList; + } + } + + #endregion + + #region IUsageTracking Members + + public UsageTracking UsageTracker { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/Laptop.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/Laptop.cs new file mode 100644 index 00000000..3c9fc714 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/PC/Laptop.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharpPro; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Core; + +namespace PepperDash.Essentials.Devices.Common +{ + /// + /// This DVD class should cover most IR, one-way DVD and Bluray fuctions + /// + public class Laptop : Device, IHasFeedback, IRoutingOutputs, IAttachVideoStatus, IUiDisplayInfo, IUsageTracking + { + public uint DisplayUiType { get { return DisplayUiConstants.TypeLaptop; } } + public string IconName { get; set; } + public BoolFeedback HasPowerOnFeedback { get; private set; } + + public RoutingOutputPort AnyVideoOut { get; private set; } + + #region IRoutingOutputs Members + + /// + /// Options: hdmi + /// + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + public Laptop(string key, string name) + : base(key, name) + { + IconName = "Laptop"; + HasPowerOnFeedback = new BoolFeedback("HasPowerFeedback", + () => this.GetVideoStatuses() != VideoStatusOutputs.NoStatus); + OutputPorts = new RoutingPortCollection(); + OutputPorts.Add(AnyVideoOut = new RoutingOutputPort(RoutingPortNames.AnyOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.None, 0, this)); + } + + #region IHasFeedback Members + + /// + /// Passes through the VideoStatuses list + /// + public FeedbackCollection Feedbacks + { + get + { + var newList = new FeedbackCollection(); + newList.AddRange(this.GetVideoStatuses().ToList()); + return newList; + } + } + + #endregion + + #region IUsageTracking Members + + public UsageTracking UsageTracker { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Power Controllers/DigitalLoggerPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Power Controllers/DigitalLoggerPropertiesConfig.cs new file mode 100644 index 00000000..1fae442b --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Power Controllers/DigitalLoggerPropertiesConfig.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common +{ + /// + /// + /// + public class DigitalLoggerPropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public ControlPropertiesConfig Control { get; set; } + public string userName { get; set; } + public string password { get; set; } + public string address { get; set; } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Power Controllers/Digitallogger.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Power Controllers/Digitallogger.cs new file mode 100644 index 00000000..e48a5b7b --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Power Controllers/Digitallogger.cs @@ -0,0 +1,313 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using System.Text.RegularExpressions; +using Crestron.SimplSharp.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + + +namespace PepperDash.Essentials.Devices.Common +{ + + public class DigitalLogger : Device + { + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + + private HttpClient WebClient; + public string userName; + public string password; + public string address; + private bool OnlineStatus; + public BoolFeedback OnlineFeedback; + private ushort CurrentPreset; + public IntFeedback PresetFeedback; + + public Dictionary CircuitStatus; + public uint CircuitCount; + + public Dictionary CircuitNameFeedbacks { get; private set; } + public Dictionary CircuitIsCritical{ get; private set; } + public Dictionary CircuitState { get; private set; } + + // new public Dictionary LevelControlPoints { get; private set; } + // public List PresetList = new List(); + + public bool isSubscribed; + + private CTimer SubscriptionTimer; + + CrestronQueue CommandQueue; + + bool CommandQueueInProgress = false; + + //new public Dictionary DialerControlPoints { get; private set; } + + //new public Dictionary SwitcherControlPoints { get; private set; } + + /// + /// Shows received lines as hex + /// + public bool ShowHexResponse { get; set; } + + public DigitalLogger(string key, string name, DigitalLoggerPropertiesConfig props) : + base(key, name) + { + CircuitCount = 8; + this.userName = props.userName; + this.password = props.password; + CommandQueue = new CrestronQueue(100); + + WebClient = new HttpClient(); + WebClient.UserName = this.userName; + WebClient.Password = this.password; + this.address = props.address; + WebClient.HostAddress = props.address; + + + + } + + public override bool CustomActivate() + { + /* + Communication.Connect(); + CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); }; + CommunicationMonitor.Start(); + */ + + OnlineFeedback = new BoolFeedback(() => { return OnlineStatus; }); + CircuitStatus = new Dictionary(); + CircuitNameFeedbacks = new Dictionary(); + CircuitIsCritical = new Dictionary(); + CircuitState = new Dictionary(); + for (uint i = 0; i < CircuitCount; i++) + { + uint circuit = i; + CircuitStatus[circuit] = new DigitalLoggerCircuit(); + CircuitNameFeedbacks[circuit] = new StringFeedback(() => { + if (CircuitStatus[circuit].name != null) + { + return CircuitStatus[circuit].name; + } + else + { + return ""; + } + }); + CircuitIsCritical[circuit] = new BoolFeedback(() => + { + if (CircuitStatus[circuit].critical != null) + { + return CircuitStatus[circuit].critical; + } + else + { + return false; + } + }); + CircuitState[circuit] = new BoolFeedback(() => + { + if (CircuitStatus[circuit].state != null) + { + return CircuitStatus[circuit].state; + } + else + { + return false; + } + }); + PollCircuit(circuit); + } + + CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + + + + return true; + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString()); + + if (e.Client.IsConnected) + { + OnlineStatus = true; + OnlineFeedback.FireUpdate(); + } + else + { + OnlineStatus = false; + OnlineFeedback.FireUpdate(); + if (SubscriptionTimer != null) + { + SubscriptionTimer.Stop(); + SubscriptionTimer = null; + } + + isSubscribed = false; + CommandQueue.Clear(); + CommandQueueInProgress = false; + } + } + + public void PollCircuit(uint circuit) + { + try + { + string PollCircuitResponse = SendRequest(String.Format("/restapi/relay/outlets/{0}/", circuit)); + CircuitStatus[circuit] = JsonConvert.DeserializeObject(PollCircuitResponse); + DigitalLoggerCircuit temp = CircuitStatus[circuit]; + Debug.Console(2, this, "DigitalLogger Circuit {0} Name: {1} State:{2}'", circuit, CircuitStatus[circuit].name, CircuitStatus[circuit].state); + CircuitNameFeedbacks[circuit].FireUpdate(); + CircuitState[circuit].FireUpdate(); + CircuitIsCritical[circuit].FireUpdate(); + } + catch (Exception e) + { + Debug.Console(0, this, "PollCircuit {0}", e); + + } + } + void Port_LineReceived(string response, HTTP_CALLBACK_ERROR error) + { + + + } + + public string SendRequest(string s) + { + HttpClientRequest request = new HttpClientRequest(); + string url = string.Format("http://{0}{1}", this.address, s); + request.Url = new UrlParser(url); + HttpClientResponse response = WebClient.Dispatch(request); + + Debug.Console(2, this, "DigitalLogger TX:\n'{0}'\nRX:\n'{1}'", url, response.ContentString); + return response.ContentString; + } + /// + /// Sends a command to the DSP (with delimiter appended) + /// + /// Command to send + /// + public void SendLine(string s) + { + + HttpClientRequest request = new HttpClientRequest(); + string url = string.Format("http://{0}{1}", this.address, s); + request.Url = new UrlParser(url); + + HttpClientResponse response = WebClient.Dispatch(request); + + Debug.Console(2, this, "DigitalLogger TX:\n'{0}'\nRX:\n'{1}'", url, response.ContentString); + + } + + public void CycleCircuit(uint circuit) + { + SendLine(String.Format("/outlet?{0}=CCL", circuit)); + //PollCircuit(circuit); + + } + + public void TurnOnCircuit(uint circuit) + { + SendLine(String.Format("/outlet?{0}=ON", circuit)); + //PollCircuit(circuit); + } + + public void TurnOffCircuit(uint circuit) + { + SendLine(String.Format("/outlet?{0}=Off", circuit)); + //PollCircuit(circuit); + } + + /// + /// Adds a command from a child module to the queue + /// + /// Command object from child module + public void EnqueueCommand(QueuedCommand commandToEnqueue) + { + CommandQueue.Enqueue(commandToEnqueue); + //Debug.Console(1, this, "Command (QueuedCommand) Enqueued '{0}'. CommandQueue has '{1}' Elements.", commandToEnqueue.Command, CommandQueue.Count); + + if(!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Adds a raw string command to the queue + /// + /// + public void EnqueueCommand(string command) + { + CommandQueue.Enqueue(command); + //Debug.Console(1, this, "Command (string) Enqueued '{0}'. CommandQueue has '{1}' Elements.", command, CommandQueue.Count); + + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + } + + /// + /// Sends the next queued command to the DSP + /// + void SendNextQueuedCommand() + { + if (Communication.IsConnected && !CommandQueue.IsEmpty) + { + CommandQueueInProgress = true; + + if (CommandQueue.Peek() is QueuedCommand) + { + QueuedCommand nextCommand = new QueuedCommand(); + + nextCommand = (QueuedCommand)CommandQueue.Peek(); + + SendLine(nextCommand.Command); + } + else + { + string nextCommand = (string)CommandQueue.Peek(); + + SendLine(nextCommand); + } + } + + } + + + + public void CallPreset(ushort presetNumber) + { + SendLine(string.Format("Preset.Take = {0}", presetNumber)); + // SendLine("cgp 1"); + } + + public class QueuedCommand + { + public string Command { get; set; } + public string AttributeCode { get; set; } + // public QscDspControlPoint ControlPoint { get; set; } + } + + public class DigitalLoggerCircuit + { + public string name; + public bool locked; + public bool critical; + public bool transient_state; + public bool physical_state; + //public int cycle_delay; + public bool state; + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Properties/AssemblyInfo.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a7df216a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyTitle("Essentials_Devices_Common")] +[assembly: AssemblyCompany("PepperDash Technology Corp")] +[assembly: AssemblyProduct("Essentials_Devices_Common")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyVersion("1.3.*")] \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Properties/ControlSystem.cfg b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Properties/ControlSystem.cfg new file mode 100644 index 00000000..e69de29b diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/SetTopBox/IRSetTopBoxBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/SetTopBox/IRSetTopBoxBase.cs new file mode 100644 index 00000000..4ec0264d --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/SetTopBox/IRSetTopBoxBase.cs @@ -0,0 +1,343 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Presets; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Common +{ + public class IRSetTopBoxBase : Device, ISetTopBoxControls, IUiDisplayInfo, IRoutingOutputs, IUsageTracking + { + public IrOutputPortController IrPort { get; private set; } + + public uint DisplayUiType { get { return DisplayUiConstants.TypeDirecTv; } } + + + public bool HasPresets { get; set; } + public bool HasDvr { get; set; } + public bool HasDpad { get; set; } + public bool HasNumeric { get; set; } + + public DevicePresetsModel PresetsModel { get; private set; } + + public IRSetTopBoxBase(string key, string name, IrOutputPortController portCont, + SetTopBoxPropertiesConfig props) + : base(key, name) + { + IrPort = portCont; + DeviceManager.AddDevice(portCont); + + HasPresets = props.HasPresets; + HasDvr = props.HasDvr; + HasDpad = props.HasDpad; + HasNumeric = props.HasNumeric; + + HasKeypadAccessoryButton1 = true; + KeypadAccessoryButton1Command = "Dash"; + KeypadAccessoryButton1Label = "-"; + + HasKeypadAccessoryButton2 = true; + KeypadAccessoryButton2Command = "NumericEnter"; + KeypadAccessoryButton2Label = "Enter"; + + AnyVideoOut = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + AnyAudioOut = new RoutingOutputPort(RoutingPortNames.AnyAudioOut, eRoutingSignalType.Audio, + eRoutingPortConnectionType.DigitalAudio, null, this); + OutputPorts = new RoutingPortCollection { AnyVideoOut, AnyAudioOut }; + + } + + public void LoadPresets(string filePath) + { + PresetsModel = new DevicePresetsModel(Key + "-presets", this, filePath); + DeviceManager.AddDevice(PresetsModel); + } + + + #region ISetTopBoxControls Members + + public void DvrList(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_DVR, pressRelease); + } + + public void Replay(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_REPLAY, pressRelease); + } + + #endregion + + #region IDPad Members + + public void Up(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_UP_ARROW, pressRelease); + } + + public void Down(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_DN_ARROW, pressRelease); + } + + public void Left(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_LEFT_ARROW, pressRelease); + } + + public void Right(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_RIGHT_ARROW, pressRelease); + } + + public void Select(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_ENTER, pressRelease); + } + + public void Menu(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_MENU, pressRelease); + } + + public void Exit(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_EXIT, pressRelease); + } + + #endregion + + #region INumericKeypad Members + + public void Digit0(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_0, pressRelease); + } + + public void Digit1(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_1, pressRelease); + } + + public void Digit2(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_2, pressRelease); + } + + public void Digit3(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_3, pressRelease); + } + + public void Digit4(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_4, pressRelease); + } + + public void Digit5(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_5, pressRelease); + } + + public void Digit6(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_6, pressRelease); + } + + public void Digit7(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_7, pressRelease); + } + + public void Digit8(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_8, pressRelease); + } + + public void Digit9(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_9, pressRelease); + } + + /// + /// Defaults to true + /// + public bool HasKeypadAccessoryButton1 { get; set; } + + /// + /// Defaults to "-" + /// + public string KeypadAccessoryButton1Label { get; set; } + + + /// + /// Defaults to "Dash" + /// + public string KeypadAccessoryButton1Command { get; set; } + + public void KeypadAccessoryButton1(bool pressRelease) + { + IrPort.PressRelease(KeypadAccessoryButton1Command, pressRelease); + } + + /// + /// Defaults to true + /// + public bool HasKeypadAccessoryButton2 { get; set; } + + /// + /// Defaults to "Enter" + /// + public string KeypadAccessoryButton2Label { get; set; } + + + /// + /// Defaults to "Enter" + /// + public string KeypadAccessoryButton2Command { get; set; } + + public void KeypadAccessoryButton2(bool pressRelease) + { + IrPort.PressRelease(KeypadAccessoryButton2Command, pressRelease); + } + + #endregion + + #region ISetTopBoxNumericKeypad Members + + /// + /// Corresponds to "dash" IR command + /// + public void Dash(bool pressRelease) + { + IrPort.PressRelease("dash", pressRelease); + } + + /// + /// Corresponds to "numericEnter" IR command + /// + public void KeypadEnter(bool pressRelease) + { + IrPort.PressRelease("numericEnter", pressRelease); + } + + #endregion + + #region IChannelFunctions Members + + public void ChannelUp(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_CH_PLUS, pressRelease); + } + + public void ChannelDown(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_CH_MINUS, pressRelease); + } + + public void LastChannel(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_LAST, pressRelease); + } + + public void Guide(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_GUIDE, pressRelease); + } + + public void Info(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_INFO, pressRelease); + } + + #endregion + + #region IColorFunctions Members + + public void Red(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_RED, pressRelease); + } + + public void Green(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_GREEN, pressRelease); + } + + public void Yellow(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_YELLOW, pressRelease); + } + + public void Blue(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_BLUE, pressRelease); + } + + #endregion + + #region IRoutingOutputs Members + + public RoutingOutputPort AnyVideoOut { get; private set; } + public RoutingOutputPort AnyAudioOut { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + #region ITransport Members + + public void ChapMinus(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_REPLAY, pressRelease); + } + + public void ChapPlus(bool pressRelease) + { + } + + public void FFwd(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_FSCAN, pressRelease); + } + + public void Pause(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_RSCAN, pressRelease); + } + + public void Play(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_PLAY, pressRelease); + } + + public void Record(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_RECORD, pressRelease); + } + + public void Rewind(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_RSCAN, pressRelease); + } + + public void Stop(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_STOP, pressRelease); + } + + #endregion + + #region IUsageTracking Members + + public UsageTracking UsageTracker { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/SetTopBox/SetTopBoxPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/SetTopBox/SetTopBoxPropertiesConfig.cs new file mode 100644 index 00000000..ae9a6709 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/SetTopBox/SetTopBoxPropertiesConfig.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Devices.Common +{ + public class SetTopBoxPropertiesConfig : PepperDash.Essentials.Core.Config.SourceDevicePropertiesConfigBase + { + public bool HasPresets { get; set; } + public bool HasDvr { get; set; } + public bool HasDpad { get; set; } + public bool HasNumeric { get; set; } + + public ControlPropertiesConfig Control { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Streaming/AppleTV.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Streaming/AppleTV.cs new file mode 100644 index 00000000..d8cd66b2 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Streaming/AppleTV.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Common +{ + public class AppleTV : Device, IDPad, ITransport, IUiDisplayInfo, IRoutingOutputs + { + + public IrOutputPortController IrPort { get; private set; } + public const string StandardDriverName = "Apple AppleTV-v2.ir"; + public uint DisplayUiType { get { return DisplayUiConstants.TypeAppleTv; } } + + public AppleTV(string key, string name, IrOutputPortController portCont) + : base(key, name) + { + IrPort = portCont; + DeviceManager.AddDevice(portCont); + + HdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + AnyAudioOut = new RoutingOutputPort(RoutingPortNames.AnyAudioOut, eRoutingSignalType.Audio, + eRoutingPortConnectionType.DigitalAudio, null, this); + OutputPorts = new RoutingPortCollection { HdmiOut, AnyAudioOut }; + } + + + #region IDPad Members + + public void Up(bool pressRelease) + { + IrPort.PressRelease("+", pressRelease); + } + + public void Down(bool pressRelease) + { + IrPort.PressRelease("-", pressRelease); + } + + public void Left(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_TRACK_MINUS, pressRelease); + } + + public void Right(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_TRACK_PLUS, pressRelease); + } + + public void Select(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_ENTER, pressRelease); + } + + public void Menu(bool pressRelease) + { + IrPort.PressRelease("Menu", pressRelease); + } + + public void Exit(bool pressRelease) + { + + } + + #endregion + + #region ITransport Members + + public void Play(bool pressRelease) + { + IrPort.PressRelease("PLAY/PAUSE", pressRelease); + } + + public void Pause(bool pressRelease) + { + IrPort.PressRelease("PLAY/PAUSE", pressRelease); + } + + /// + /// Not implemented + /// + /// + public void Rewind(bool pressRelease) + { + } + + /// + /// Not implemented + /// + /// + public void FFwd(bool pressRelease) + { + } + + /// + /// Not implemented + /// + /// + public void ChapMinus(bool pressRelease) + { + } + + /// + /// Not implemented + /// + /// + public void ChapPlus(bool pressRelease) + { + } + + /// + /// Not implemented + /// + /// + public void Stop(bool pressRelease) + { + } + + /// + /// Not implemented + /// + /// + public void Record(bool pressRelease) + { + } + + #endregion + + #region IRoutingOutputs Members + + public RoutingOutputPort HdmiOut { get; private set; } + public RoutingOutputPort AnyAudioOut { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Streaming/Roku.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Streaming/Roku.cs new file mode 100644 index 00000000..2d4af003 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Streaming/Roku.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; + +namespace PepperDash.Essentials.Devices.Common +{ + public class Roku2 : Device, IDPad, ITransport, IUiDisplayInfo, IRoutingOutputs + { + [Api] + public IrOutputPortController IrPort { get; private set; } + public const string StandardDriverName = "Roku XD_S.ir"; + [Api] + public uint DisplayUiType { get { return DisplayUiConstants.TypeRoku; } } + + public Roku2(string key, string name, IrOutputPortController portCont) + : base(key, name) + { + IrPort = portCont; + DeviceManager.AddDevice(portCont);; + + HdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + OutputPorts = new RoutingPortCollection { HdmiOut }; + } + + #region IDPad Members + + [Api] + public void Up(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_UP_ARROW, pressRelease); + } + + [Api] + public void Down(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_DN_ARROW, pressRelease); + } + + [Api] + public void Left(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_LEFT_ARROW, pressRelease); + } + + [Api] + public void Right(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_RIGHT_ARROW, pressRelease); + } + + [Api] + public void Select(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_ENTER, pressRelease); + } + + [Api] + public void Menu(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_MENU, pressRelease); + } + + [Api] + public void Exit(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_EXIT, pressRelease); + } + + #endregion + + #region ITransport Members + + [Api] + public void Play(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_PLAY, pressRelease); + } + + [Api] + public void Pause(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_PAUSE, pressRelease); + } + + [Api] + public void Rewind(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_RSCAN, pressRelease); + } + + [Api] + public void FFwd(bool pressRelease) + { + IrPort.PressRelease(IROutputStandardCommands.IROut_FSCAN, pressRelease); + } + + /// + /// Not implemented + /// + /// + public void ChapMinus(bool pressRelease) + { + } + + /// + /// Not implemented + /// + /// + public void ChapPlus(bool pressRelease) + { + } + + /// + /// Not implemented + /// + /// + public void Stop(bool pressRelease) + { + } + + /// + /// Not implemented + /// + /// + public void Record(bool pressRelease) + { + } + + #endregion + + #region IRoutingOutputs Members + + public RoutingOutputPort HdmiOut { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/BookingsDataClasses.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/BookingsDataClasses.cs new file mode 100644 index 00000000..46d171cf --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/BookingsDataClasses.cs @@ -0,0 +1,379 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Devices.Common.Codec; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public class CiscoCodecBookings + { + public class TotalRows + { + public string Value { get; set; } + } + + public class ResultInfo + { + public TotalRows TotalRows { get; set; } + } + + public class LastUpdated + { + string _value; + + public DateTime Value { + get + { + DateTime _valueDateTime; + try + { + _valueDateTime = DateTime.Parse(_value); + return _valueDateTime; + } + catch + { + return new DateTime(); + } + } + set + { + _value = value.ToString(); + } + } + } + + public class Id + { + public string Value { get; set; } + } + + public class Title + { + public string Value { get; set; } + } + + public class Agenda + { + public string Value { get; set; } + } + + public class Privacy + { + public string Value { get; set; } + } + + public class FirstName + { + public string Value { get; set; } + } + + public class LastName + { + public string Value { get; set; } + } + + public class Email + { + public string Value { get; set; } + } + + public class Id2 + { + public string Value { get; set; } + } + + public class Organizer + { + public FirstName FirstName { get; set; } + public LastName LastName { get; set; } + public Email Email { get; set; } + public Id2 Id { get; set; } + + public Organizer() + { + FirstName = new FirstName(); + LastName = new LastName(); + Email = new Email(); + } + } + + public class StartTime + { + public DateTime Value { get; set; } + } + + public class StartTimeBuffer + { + public string Value { get; set; } + } + + public class EndTime + { + public DateTime Value { get; set; } + } + + public class EndTimeBuffer + { + public string Value { get; set; } + } + + public class Time + { + public StartTime StartTime { get; set; } + public StartTimeBuffer StartTimeBuffer { get; set; } + public EndTime EndTime { get; set; } + public EndTimeBuffer EndTimeBuffer { get; set; } + + public Time() + { + StartTime = new StartTime(); + EndTime = new EndTime(); + } + } + + public class MaximumMeetingExtension + { + public string Value { get; set; } + } + + public class MeetingExtensionAvailability + { + public string Value { get; set; } + } + + public class BookingStatus + { + public string Value { get; set; } + } + + public class BookingStatusMessage + { + public string Value { get; set; } + } + + public class Enabled + { + public string Value { get; set; } + } + + public class Url + { + public string Value { get; set; } + } + + public class MeetingNumber + { + public string Value { get; set; } + } + + public class Password + { + public string Value { get; set; } + } + + public class HostKey + { + public string Value { get; set; } + } + + public class DialInNumbers + { + } + + public class Webex + { + public Enabled Enabled { get; set; } + public Url Url { get; set; } + public MeetingNumber MeetingNumber { get; set; } + public Password Password { get; set; } + public HostKey HostKey { get; set; } + public DialInNumbers DialInNumbers { get; set; } + } + + public class Encryption + { + public string Value { get; set; } + } + + public class Role + { + public string Value { get; set; } + } + + public class Recording + { + public string Value { get; set; } + } + + public class Number + { + public string Value { get; set; } + } + + public class Protocol + { + public string Value { get; set; } + } + + public class CallRate + { + public string Value { get; set; } + } + + public class CallType + { + public string Value { get; set; } + } + + public class Call + { + public string id { get; set; } + public Number Number { get; set; } + public Protocol Protocol { get; set; } + public CallRate CallRate { get; set; } + public CallType CallType { get; set; } + } + + public class Calls + { + public List Call {get; set;} + } + + public class ConnectMode + { + public string Value { get; set; } + } + + public class DialInfo + { + public Calls Calls { get; set; } + public ConnectMode ConnectMode { get; set; } + + public DialInfo() + { + Calls = new Calls(); + ConnectMode = new ConnectMode(); + } + } + + public class Booking + { + public string id { get; set; } + public Id Id { get; set; } + public Title Title { get; set; } + public Agenda Agenda { get; set; } + public Privacy Privacy { get; set; } + public Organizer Organizer { get; set; } + public Time Time { get; set; } + public MaximumMeetingExtension MaximumMeetingExtension { get; set; } + public MeetingExtensionAvailability MeetingExtensionAvailability { get; set; } + public BookingStatus BookingStatus { get; set; } + public BookingStatusMessage BookingStatusMessage { get; set; } + public Webex Webex { get; set; } + public Encryption Encryption { get; set; } + public Role Role { get; set; } + public Recording Recording { get; set; } + public DialInfo DialInfo { get; set; } + + public Booking() + { + Time = new Time(); + Id = new Id(); + Organizer = new Organizer(); + Title = new Title(); + Agenda = new Agenda(); + Privacy = new Privacy(); + DialInfo = new DialInfo(); + } + } + + public class BookingsListResult + { + public string status { get; set; } + public ResultInfo ResultInfo { get; set; } + //public LastUpdated LastUpdated { get; set; } + public List Booking { get; set; } + } + + public class CommandResponse + { + public BookingsListResult BookingsListResult { get; set; } + } + + public class RootObject + { + public CommandResponse CommandResponse { get; set; } + } + + /// + /// Extracts the necessary meeting values from the Cisco bookings response ans converts them to the generic class + /// + /// + /// + public static List GetGenericMeetingsFromBookingResult(List bookings) + { + var meetings = new List(); + + if (Debug.Level > 0) + { + Debug.Console(1, "Meetings List:\n"); + } + + foreach(Booking b in bookings) + { + var meeting = new Meeting(); + + if(b.Id != null) + meeting.Id = b.Id.Value; + if(b.Organizer != null) + meeting.Organizer = string.Format("{0}, {1}", b.Organizer.LastName.Value, b.Organizer.FirstName.Value); + if(b.Title != null) + meeting.Title = b.Title.Value; + if(b.Agenda != null) + meeting.Agenda = b.Agenda.Value; + if(b.Time != null) + meeting.StartTime = b.Time.StartTime.Value; + meeting.EndTime = b.Time.EndTime.Value; + if(b.Privacy != null) + meeting.Privacy = CodecCallPrivacy.ConvertToDirectionEnum(b.Privacy.Value); + +//#warning Update this ConnectMode conversion after testing onsite. Expected value is "OBTP", but in PD NYC Test scenarios, "Manual" is being returned for OBTP meetings + if (b.DialInfo.ConnectMode != null) + if (b.DialInfo.ConnectMode.Value.ToLower() == "obtp" || b.DialInfo.ConnectMode.Value.ToLower() == "manual") + meeting.IsOneButtonToPushMeeting = true; + + if (b.DialInfo.Calls.Call != null) + { + foreach (Call c in b.DialInfo.Calls.Call) + { + meeting.Calls.Add(new PepperDash.Essentials.Devices.Common.Codec.Call() + { + Number = c.Number.Value, + Protocol = c.Protocol.Value, + CallRate = c.CallRate.Value, + CallType = c.CallType.Value + }); + } + } + + + meetings.Add(meeting); + + if(Debug.Level > 0) + { + Debug.Console(1, "Title: {0}, ID: {1}, Organizer: {2}, Agenda: {3}", meeting.Title, meeting.Id, meeting.Organizer, meeting.Agenda); + Debug.Console(1, " Start Time: {0}, End Time: {1}, Duration: {2}", meeting.StartTime, meeting.EndTime, meeting.Duration); + Debug.Console(1, " Joinable: {0}\n", meeting.Joinable); + } + } + + meetings.OrderBy(m => m.StartTime); + + return meetings; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CallHistoryDataClasses.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CallHistoryDataClasses.cs new file mode 100644 index 00000000..4fc07bd2 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CallHistoryDataClasses.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public class CiscoCallHistory + { + public class CallbackNumber + { + public string Value { get; set; } + } + + public class DisplayName + { + public string Value { get; set; } + } + + public class LastOccurrenceStartTime + { + public DateTime Value { get; set; } + } + + public class LastOccurrenceDaysAgo + { + public string Value { get; set; } + } + + public class LastOccurrenceHistoryId + { + public string Value { get; set; } + } + + public class OccurrenceType + { + public string Value { get; set; } + } + + public class IsAcknowledged + { + public string Value { get; set; } + } + + public class OccurrenceCount + { + public string Value { get; set; } + } + + public class Entry + { + public string id { get; set; } + public CallbackNumber CallbackNumber { get; set; } + public DisplayName DisplayName { get; set; } + public LastOccurrenceStartTime LastOccurrenceStartTime { get; set; } + public LastOccurrenceDaysAgo LastOccurrenceDaysAgo { get; set; } + public LastOccurrenceHistoryId LastOccurrenceHistoryId { get; set; } + public OccurrenceType OccurrenceType { get; set; } + public IsAcknowledged IsAcknowledged { get; set; } + public OccurrenceCount OccurrenceCount { get; set; } + } + + public class Offset + { + public string Value { get; set; } + } + + public class Limit + { + public string Value { get; set; } + } + + public class ResultInfo + { + public Offset Offset { get; set; } + public Limit Limit { get; set; } + } + + public class CallHistoryRecentsResult + { + public string status { get; set; } + public List Entry { get; set; } + public ResultInfo ResultInfo { get; set; } + } + + public class CommandResponse + { + public CallHistoryRecentsResult CallHistoryRecentsResult { get; set; } + } + + public class RootObject + { + public CommandResponse CommandResponse { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs new file mode 100644 index 00000000..12819336 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCamera.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Devices.Common.Cameras; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + public class CiscoFarEndCamera : CameraBase, IHasCameraPtzControl, IAmFarEndCamera + { + protected CiscoSparkCodec ParentCodec { get; private set; } + + protected string CallId { + get + { + return (ParentCodec as CiscoSparkCodec).GetCallId(); + } + } + + public CiscoFarEndCamera(string key, string name, CiscoSparkCodec codec) + : base(key, name) + { + Capabilities = eCameraCapabilities.Pan | eCameraCapabilities.Tilt | eCameraCapabilities.Zoom; + + ParentCodec = codec; + } + + #region IHasCameraPtzControl Members + + public void PositionHome() + { + // Not supported on far end camera + } + + #endregion + + #region IHasCameraPanControl Members + + public void PanLeft() + { + ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Left CallId: {0}", CallId)); + } + + public void PanRight() + { + ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Right CallId: {0}", CallId)); + } + + public void PanStop() + { + Stop(); + } + + #endregion + + #region IHasCameraTiltControl Members + + public void TiltDown() + { + ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Down CallId: {0}", CallId)); + } + + public void TiltUp() + { + ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: Up CallId: {0}", CallId)); + } + + public void TiltStop() + { + Stop(); + } + + #endregion + + #region IHasCameraZoomControl Members + + public void ZoomIn() + { + ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: ZoomIn CallId: {0}", CallId)); + } + + public void ZoomOut() + { + ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Move Value: ZoomOut CallId: {0}", CallId)); + } + + public void ZoomStop() + { + Stop(); + } + + #endregion + + + void Stop() + { + ParentCodec.SendText(string.Format("xCommand Call FarEndControl Camera Stop CallId: {0}", CallId)); + } + } + + public class CiscoSparkCamera : CameraBase, IHasCameraPtzControl, IHasCameraFocusControl + { + /// + /// The codec this camera belongs to + /// + protected CiscoSparkCodec ParentCodec { get; private set; } + + /// + /// The ID of the camera on the codec + /// + protected uint CameraId { get; private set; } + + /// + /// Valid range 1-15 + /// + protected uint PanSpeed { get; private set; } + + /// + /// Valid range 1-15 + /// + protected uint TiltSpeed { get; private set; } + + /// + /// Valid range 1-15 + /// + protected uint ZoomSpeed { get; private set; } + + private bool isPanning; + + private bool isTilting; + + private bool isZooming; + + private bool isFocusing; + + private bool isMoving + { + get + { + return isPanning || isTilting || isZooming || isFocusing; + + } + } + + public CiscoSparkCamera(string key, string name, CiscoSparkCodec codec, uint id) + : base(key, name) + { + // Default to all capabilties + Capabilities = eCameraCapabilities.Pan | eCameraCapabilities.Tilt | eCameraCapabilities.Zoom | eCameraCapabilities.Focus; + + ParentCodec = codec; + + CameraId = id; + + // Set default speeds + PanSpeed = 7; + TiltSpeed = 7; + ZoomSpeed = 7; + } + + + // Takes a string from the camera capabilities value and converts from "ptzf" to enum bitmask + public void SetCapabilites(string capabilites) + { + var c = capabilites.ToLower(); + + if (c.Contains("p")) + Capabilities = Capabilities | eCameraCapabilities.Pan; + + if (c.Contains("t")) + Capabilities = Capabilities | eCameraCapabilities.Tilt; + + if (c.Contains("z")) + Capabilities = Capabilities | eCameraCapabilities.Zoom; + + if (c.Contains("f")) + Capabilities = Capabilities | eCameraCapabilities.Focus; + } + + #region IHasCameraPtzControl Members + + public void PositionHome() + { + // Not supported on Internal Spark Camera + + + } + + #endregion + + #region IHasCameraPanControl Members + + public void PanLeft() + { + if (!isMoving) + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Left PanSpeed: {1}", CameraId, PanSpeed)); + isPanning = true; + } + } + + public void PanRight() + { + if (!isMoving) + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Right PanSpeed: {1}", CameraId, PanSpeed)); + isPanning = true; + } + } + + public void PanStop() + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Pan: Stop", CameraId)); + isPanning = false; + } + + #endregion + + + + #region IHasCameraTiltControl Members + + public void TiltDown() + { + if (!isMoving) + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Down TiltSpeed: {1}", CameraId, TiltSpeed)); + isTilting = true; + } + } + + public void TiltUp() + { + if (!isMoving) + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Up TiltSpeed: {1}", CameraId, TiltSpeed)); + isTilting = true; + } + } + + public void TiltStop() + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Tilt: Stop", CameraId)); + isTilting = false; + } + + #endregion + + #region IHasCameraZoomControl Members + + public void ZoomIn() + { + if (!isMoving) + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: In ZoomSpeed: {1}", CameraId, ZoomSpeed)); + isZooming = true; + } + } + + public void ZoomOut() + { + if (!isMoving) + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: Out ZoomSpeed: {1}", CameraId, ZoomSpeed)); + isZooming = true; + } + } + + public void ZoomStop() + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Zoom: Stop", CameraId)); + isZooming = false; + } + + #endregion + + #region IHasCameraFocusControl Members + + public void FocusNear() + { + if (!isMoving) + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Near", CameraId)); + isFocusing = true; + } + } + + public void FocusFar() + { + if (!isMoving) + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Far", CameraId)); + isFocusing = true; + } + } + + public void FocusStop() + { + ParentCodec.SendText(string.Format("xCommand Camera Ramp CameraId: {0} Focus: Stop", CameraId)); + isFocusing = false; + } + + public void TriggerAutoFocus() + { + ParentCodec.SendText(string.Format("xCommand Camera TriggerAutofocus CameraId: {0}", CameraId)); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs.orig b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs.orig new file mode 100644 index 00000000..3abc8d01 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodec.cs.orig @@ -0,0 +1,1359 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Net.Https; +using Crestron.SimplSharp.CrestronXml; +using Crestron.SimplSharp.CrestronXml.Serialization; +using Newtonsoft.Json; +<<<<<<< HEAD +//using Cisco_One_Button_To_Push; +//using Cisco_SX80_Corporate_Phone_Book; +======= +>>>>>>> origin/feature/ecs-342 + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.Occupancy; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration }; + + public class CiscoCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfview + { + public event EventHandler DirectoryResultReturned; + + public IBasicCommunication Communication { get; private set; } + public CommunicationGather PortGather { get; private set; } + public CommunicationGather JsonGather { get; private set; } + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + public BoolFeedback StandbyIsOnFeedback { get; private set; } + + public BoolFeedback RoomIsOccupiedFeedback { get; private set; } + + public IntFeedback PeopleCountFeedback { get; private set; } + + public BoolFeedback SpeakerTrackIsOnFeedback { get; private set; } + + public BoolFeedback SelfviewIsOnFeedback { get; private set; } + + public StringFeedback SelfviewPipPositionFeedback { get; private set; } + + public StringFeedback LocalLayoutFeedback { get; private set; } + + private CodecCommandWithLabel CurrentSelfviewPipPosition; + + private CodecCommandWithLabel CurrentLocalLayout; + + /// + /// List the available positions for the selfview PIP window + /// + public List SelfviewPipPositions = new List() + { + new CodecCommandWithLabel("CenterLeft", "Center Left"), + new CodecCommandWithLabel("CenterRight", "Center Right"), + new CodecCommandWithLabel("LowerLeft", "Lower Left"), + new CodecCommandWithLabel("LowerRight", "Lower Right"), + new CodecCommandWithLabel("UpperCenter", "Upper Center"), + new CodecCommandWithLabel("UpperLeft", "Upper Left"), + new CodecCommandWithLabel("UpperRight", "Upper Right"), + }; + + /// + /// Lists the available options for local layout + /// + public List LocalLayouts = new List() + { + new CodecCommandWithLabel("auto", "Auto"), + //new CiscoCodecLocalLayout("custom", "Custom"), // Left out for now + new CodecCommandWithLabel("equal","Equal"), + new CodecCommandWithLabel("overlay","Overlay"), + new CodecCommandWithLabel("prominent","Prominent"), + new CodecCommandWithLabel("single","Single") + }; + + private CiscoCodecConfiguration.RootObject CodecConfiguration; + + private CiscoCodecStatus.RootObject CodecStatus; + + public CodecCallHistory CallHistory { get; private set; } + + public CodecCallFavorites CallFavorites { get; private set; } + + public CodecDirectory DirectoryRoot { get; private set; } + + public CodecScheduleAwareness CodecSchedule { get; private set; } + + /// + /// Gets and returns the scaled volume of the codec + /// + protected override Func VolumeLevelFeedbackFunc + { + get + { + return () => CrestronEnvironment.ScaleWithLimits(CodecStatus.Status.Audio.Volume.IntValue, 100, 0, 65535, 0); + } + } + + protected override Func PrivacyModeIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Audio.Microphones.Mute.BoolValue; + } + } + + protected Func StandbyStateFeedbackFunc + { + get + { + return () => CodecStatus.Status.Standby.State.BoolValue; + } + } + + /// + /// Gets the value of the currently shared source, or returns null + /// + protected override Func SharingSourceFeedbackFunc + { +#warning verify that source feedback to room works from codec + get + { + return () => PresentationSourceKey; + } + } + + protected override Func MuteFeedbackFunc + { + get + { + return () => CodecStatus.Status.Audio.VolumeMute.BoolValue; + } + } + + protected Func RoomIsOccupiedFeedbackFunc + { + get + { + return () => CodecStatus.Status.RoomAnalytics.PeoplePresence.BoolValue; + } + } + + protected Func PeopleCountFeedbackFunc + { + get + { + return () => CodecStatus.Status.RoomAnalytics.PeopleCount.Current.IntValue; + } + } + + protected Func SpeakerTrackIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Cameras.SpeakerTrack.Status.BoolValue; + } + } + + protected Func SelfViewIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Video.Selfview.Mode.BoolValue; + } + } + + protected Func SelfviewPipPositionFeedbackFunc + { + get + { + return () => CurrentSelfviewPipPosition.Label; + } + } + + protected Func LocalLayoutFeedbackFunc + { + get + { + return () => CurrentLocalLayout.Label; + } + } + + private string CliFeedbackRegistrationExpression; + + private CodecSyncState SyncState; + + private CodecPhonebookSyncState PhonebookSyncState; + + private StringBuilder JsonMessage; + + private bool JsonFeedbackMessageIsIncoming; + + public bool CommDebuggingIsOn; + + string Delimiter = "\r\n"; + + int PresentationSource; + + string PresentationSourceKey; + + string PhonebookMode = "Local"; // Default to Local + + int PhonebookResultsLimit = 255; // Could be set later by config. + + CTimer LoginMessageReceived; + + public RoutingInputPort CodecOsdIn { get; private set; } + public RoutingInputPort HdmiIn1 { get; private set; } + public RoutingInputPort HdmiIn2 { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + // Constructor for IBasicCommunication + public CiscoCodec(string key, string name, IBasicCommunication comm, CiscoCodecPropertiesConfig props ) + : base(key, name) + { + StandbyIsOnFeedback = new BoolFeedback(StandbyStateFeedbackFunc); + RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); + PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc); + SpeakerTrackIsOnFeedback = new BoolFeedback(SpeakerTrackIsOnFeedbackFunc); + SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); + SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); + LocalLayoutFeedback = new StringFeedback(LocalLayoutFeedbackFunc); + + Communication = comm; + + LoginMessageReceived = new CTimer(DisconnectClientAndReconnect, 5000); + + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "xStatus SystemUnit Software Version\r"); + } + + DeviceManager.AddDevice(CommunicationMonitor); + + PhonebookMode = props.PhonebookMode; + + SyncState = new CodecSyncState(key + "--Sync"); + + PhonebookSyncState = new CodecPhonebookSyncState(key + "--PhonebookSync"); + + SyncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted); + + PortGather = new CommunicationGather(Communication, Delimiter); + PortGather.IncludeDelimiter = true; + PortGather.LineReceived += this.Port_LineReceived; + + //CodecObtp = new CiscoOneButtonToPush(); + + //PhoneBook = new Corporate_Phone_Book(); + + CodecConfiguration = new CiscoCodecConfiguration.RootObject(); + CodecStatus = new CiscoCodecStatus.RootObject(); + + CodecInfo = new CiscoCodecInfo(CodecStatus, CodecConfiguration); + + CallHistory = new CodecCallHistory(); + + if (props.Favorites != null) + { + CallFavorites = new CodecCallFavorites(); + CallFavorites.Favorites = props.Favorites; + } + + DirectoryRoot = new CodecDirectory(); + + CodecSchedule = new CodecScheduleAwareness(); + + //Set Feedback Actions + CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate; + CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate; + CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate; + CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate; + CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate; + CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate; + CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = SpeakerTrackIsOnFeedback.FireUpdate; + CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate; + CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus; + CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout; + } + + /// + /// Starts the HTTP feedback server and syncronizes state of codec + /// + /// + public override bool CustomActivate() + { + CrestronConsole.AddNewConsoleCommand(SendText, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); + + //CommDebuggingIsOn = true; + Communication.Connect(); + var socket = Communication as ISocketStatus; + if (socket != null) + { + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + + CommunicationMonitor.Start(); + + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); + HdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this); + HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this); + + HdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this); + + InputPorts.Add(CodecOsdIn); + InputPorts.Add(HdmiIn1); + InputPorts.Add(HdmiIn2); + OutputPorts.Add(HdmiOut); + + + string prefix = "xFeedback register "; + + CliFeedbackRegistrationExpression = + prefix + "/Configuration" + Delimiter + + prefix + "/Status/Audio" + Delimiter + + prefix + "/Status/Call" + Delimiter + + prefix + "/Status/Conference/Presentation" + Delimiter + + prefix + "/Status/Cameras/SpeakerTrack" + Delimiter + + prefix + "/Status/RoomAnalytics" + Delimiter + + prefix + "/Status/Standby" + Delimiter + + prefix + "/Status/Video/Selfview" + Delimiter + + prefix + "/Bookings" + Delimiter + + prefix + "/Event/CallDisconnect" + Delimiter; + + return base.CustomActivate(); + } + + /// + /// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc. + /// + /// + /// + void SyncState_InitialSyncCompleted(object sender, EventArgs e) + { + // Fire the ready event + SetIsReady(); + //CommDebuggingIsOn = false; + + GetCallHistory(); + + GetPhonebook(null); + + GetBookings(null); + } + + public void SetCommDebug(string s) + { + if (s == "1") + { + CommDebuggingIsOn = true; + Debug.Console(0, this, "Comm Debug Enabled."); + } + else + { + CommDebuggingIsOn = false; + Debug.Console(0, this, "Comm Debug Disabled."); + } + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + if (e.Client.IsConnected) + { + //LoginMessageReceived.Reset(); + } + else + { + SyncState.CodecDisconnected(); + PhonebookSyncState.CodecDisconnected(); + } + } + + void DisconnectClientAndReconnect(object o) + { + Debug.Console(0, this, "Disconnecting and Reconnecting to codec."); + + Communication.Disconnect(); + + CrestronEnvironment.Sleep(2000); + + Communication.Connect(); + } + + /// + /// Gathers responses from the codec (including the delimiter. Responses are checked to see if they contain JSON data and if so, the data is collected until a complete JSON + /// message is received before forwarding the message to be deserialized. + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + if (CommDebuggingIsOn) + { + if(!JsonFeedbackMessageIsIncoming) + Debug.Console(1, this, "RX: '{0}'", args.Text); + } + + if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message + { + JsonFeedbackMessageIsIncoming = true; + + if (CommDebuggingIsOn) + Debug.Console(1, this, "Incoming JSON message..."); + + JsonMessage = new StringBuilder(); + } + else if (args.Text == "}" + Delimiter) // Check for the end of a JSON message + { + JsonFeedbackMessageIsIncoming = false; + + JsonMessage.Append(args.Text); + + if (CommDebuggingIsOn) + Debug.Console(1, this, "Complete JSON Received:\n{0}", JsonMessage.ToString()); + + // Forward the complete message to be deserialized + DeserializeResponse(JsonMessage.ToString()); + return; + } + + if(JsonFeedbackMessageIsIncoming) + { + JsonMessage.Append(args.Text); + + //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); + return; + } + + if (!SyncState.InitialSyncComplete) + { + switch (args.Text.Trim().ToLower()) // remove the whitespace + { + case "*r login successful": + { + LoginMessageReceived.Stop(); + SendText("xPreferences outputmode json"); + break; + } + case "xpreferences outputmode json": + { + if (!SyncState.InitialStatusMessageWasReceived) + SendText("xStatus"); + break; + } + case "xfeedback register /event/calldisconnect": + { + SyncState.FeedbackRegistered(); + break; + } + } + } + + } + + public void SendText(string command) + { + if (CommDebuggingIsOn) + Debug.Console(1, this, "Sending: '{0}'", command); + + Communication.SendText(command + Delimiter); + } + + void DeserializeResponse(string response) + { + try + { + // Serializer settings. We want to ignore null values and mising members + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.NullValueHandling = NullValueHandling.Ignore; + settings.MissingMemberHandling = MissingMemberHandling.Ignore; + settings.ObjectCreationHandling = ObjectCreationHandling.Auto; + + if (response.IndexOf("\"Status\":{") > -1) + { + // Status Message + + // Temp object so we can inpsect for call data before simply deserializing + CiscoCodecStatus.RootObject tempCodecStatus = new CiscoCodecStatus.RootObject(); + + JsonConvert.PopulateObject(response, tempCodecStatus); + + // Check to see if this is a call status message received after the initial status message + if (tempCodecStatus.Status.Call.Count > 0) + { + // Iterate through the call objects in the response + foreach (CiscoCodecStatus.Call call in tempCodecStatus.Status.Call) + { + var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(call.id)); + + if (tempActiveCall != null) + { + bool changeDetected = false; + + // Update properties of ActiveCallItem + if(call.Status != null) + if (!string.IsNullOrEmpty(call.Status.Value)) + { + eCodecCallStatus newStatus = eCodecCallStatus.Unknown; + + newStatus = CodecCallStatus.ConvertToStatusEnum(call.Status.Value); + + if (newStatus != eCodecCallStatus.Unknown) + { + changeDetected = true; + SetNewCallStatusAndFireCallStatusChange(newStatus, tempActiveCall); + } + + if (newStatus == eCodecCallStatus.Connected) + GetCallHistory(); + } + if (call.CallType != null) + if (!string.IsNullOrEmpty(call.CallType.Value)) + { + tempActiveCall.Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value); + changeDetected = true; + } + if (call.DisplayName != null) + if (!string.IsNullOrEmpty(call.DisplayName.Value)) + { + tempActiveCall.Name = call.DisplayName.Value; + changeDetected = true; + } + if (call.Direction != null) + { + if (!string.IsNullOrEmpty(call.Direction.Value)) + { + tempActiveCall.Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value); + changeDetected = true; + } + } + + if (changeDetected) + { + ListCalls(); + } + } + else if( call.ghost == null ) // if the ghost value is present the call has ended already + { + // Create a new call item + var newCallItem = new CodecActiveCallItem() + { + Id = call.id, + Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value), + Name = call.DisplayName.Value, + Number = call.RemoteNumber.Value, + Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value), + Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value) + }; + + // Add it to the ActiveCalls List + ActiveCalls.Add(newCallItem); + + ListCalls(); + + SetNewCallStatusAndFireCallStatusChange(newCallItem.Status, newCallItem); + } + + } + + } + + JsonConvert.PopulateObject(response, CodecStatus); + + if (!SyncState.InitialStatusMessageWasReceived) + { + SyncState.InitialStatusMessageReceived(); + + if (!SyncState.InitialConfigurationMessageWasReceived) + SendText("xConfiguration"); + } + } + else if (response.IndexOf("\"Configuration\":{") > -1) + { + // Configuration Message + + JsonConvert.PopulateObject(response, CodecConfiguration); + + if (!SyncState.InitialConfigurationMessageWasReceived) + { + SyncState.InitialConfigurationMessageReceived(); + if (!SyncState.FeedbackWasRegistered) + { + SendText(CliFeedbackRegistrationExpression); + } + } + + } + else if (response.IndexOf("\"Event\":{") > -1) + { + // Event Message + + CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject(); + + JsonConvert.PopulateObject(response, eventReceived); + + EvalutateEvent(eventReceived); + } + else if (response.IndexOf("\"CommandResponse\":{") > -1) + { + // CommandResponse Message + + if (response.IndexOf("\"CallHistoryRecentsResult\":{") > -1) + { + var codecCallHistory = new CiscoCallHistory.RootObject(); + + JsonConvert.PopulateObject(response, codecCallHistory); + + CallHistory.ConvertCiscoCallHistoryToGeneric(codecCallHistory.CommandResponse.CallHistoryRecentsResult.Entry); + } + else if (response.IndexOf("\"CallHistoryDeleteEntryResult\":{") > -1) + { + GetCallHistory(); + } + else if (response.IndexOf("\"PhonebookSearchResult\":{") > -1) + { + var codecPhonebookResponse = new CiscoCodecPhonebook.RootObject(); + + JsonConvert.PopulateObject(response, codecPhonebookResponse); + + if (!PhonebookSyncState.InitialPhonebookFoldersWasReceived) + { + // Check if the phonebook has any folders + PhonebookSyncState.InitialPhonebookFoldersReceived(); + + PhonebookSyncState.SetPhonebookHasFolders(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.Folder.Count > 0); + + if (PhonebookSyncState.PhonebookHasFolders) + { + DirectoryRoot.AddFoldersToDirectory(CiscoCodecPhonebook.GetRootFoldersFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); + } + + // Get the number of contacts in the phonebook + GetPhonebookContacts(); + } + else if (!PhonebookSyncState.NumberOfContactsWasReceived) + { + // Store the total number of contacts in the phonebook + PhonebookSyncState.SetNumberOfContacts(Int32.Parse(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value)); + + DirectoryRoot.AddContactsToDirectory(CiscoCodecPhonebook.GetRootContactsFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); + + PhonebookSyncState.PhonebookRootEntriesReceived(); + + PrintPhonebook(DirectoryRoot); + } + else if (PhonebookSyncState.InitialSyncComplete) + { + var directoryResults = new CodecDirectory(); + + directoryResults = CiscoCodecPhonebook.ConvertCiscoPhonebookToGeneric(codecPhonebookResponse.CommandResponse.PhonebookSearchResult); + + PrintPhonebook(directoryResults); + + // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology + var handler = DirectoryResultReturned; + if (handler != null) + handler(this, new DirectoryEventArgs() { Directory = directoryResults }); + + // Fire some sort of callback delegate to the UI that requested the directory search results + } + } + else if (response.IndexOf("\"BookingsListResult\":{") > -1) + { + var codecBookings = new CiscoCodecBookings.RootObject(); + + JsonConvert.PopulateObject(response, codecBookings); + + CodecSchedule.Meetings = CiscoCodecBookings.GetGenericMeetingsFromBookingResult(codecBookings.CommandResponse.BookingsListResult.Booking); + + + } + + } + + } + catch (Exception ex) + { + Debug.Console(1, this, "Error Deserializing feedback from codec: {0}", ex); + } + } + + /// + /// Evaluates an event received from the codec + /// + /// + void EvalutateEvent(CiscoCodecEvents.RootObject eventReceived) + { + if (eventReceived.Event.CallDisconnect != null) + { + var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(eventReceived.Event.CallDisconnect.CallId.Value)); + + // Remove the call from the Active calls list + if (tempActiveCall != null) + { + ActiveCalls.Remove(tempActiveCall); + + ListCalls(); + + // Notify of the call disconnection + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, tempActiveCall); + + GetCallHistory(); + } + } + } + + public override void ExecuteSwitch(object selector) + { + (selector as Action)(); + PresentationSourceKey = selector.ToString(); + } + + protected override Func IncomingCallFeedbackFunc { get { return () => false; } } + + /// + /// Gets the first CallId or returns null + /// + /// + private string GetCallId() + { + string callId = null; + + if (ActiveCalls.Count > 1) + { + foreach (CodecActiveCallItem call in ActiveCalls) ; + } + else if (ActiveCalls.Count == 1) + callId = ActiveCalls[0].Id; + + return callId; + + } + + private void GetCallHistory() + { + SendText("xCommand CallHistory Recents Limit: 20 Order: OccurrenceTime"); + } + + /// + /// Gets the bookings for today + /// + /// + public void GetBookings(string command) + { + SendText("xCommand Bookings List Days: 1 DayOffset: 0"); + } + + /// + /// Triggers a refresh of the codec phonebook + /// + /// Just to allow this method to be called from a console command + public void GetPhonebook(string command) + { + PhonebookSyncState.CodecDisconnected(); + + DirectoryRoot = new CodecDirectory(); + + GetPhonebookFolders(); + } + + private void GetPhonebookFolders() + { + // Get Phonebook Folders (determine local/corporate from config, and set results limit) + SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", PhonebookMode)); + } + + private void GetPhonebookContacts() + { + // Get Phonebook Folders (determine local/corporate from config, and set results limit) + SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact", PhonebookMode)); + } + + /// + /// Searches the codec phonebook for all contacts matching the search string + /// + /// + public void SearchDirectory(string searchString) + { + SendText(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, PhonebookMode, PhonebookResultsLimit)); + } + + /// + /// // Get contents of a specific folder in the phonebook + /// + /// + public void GetDirectoryFolderContents(string folderId) + { + SendText(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Contact Limit: {2}", folderId, PhonebookMode, PhonebookResultsLimit)); + } + + void PrintPhonebook(CodecDirectory directory) + { + if (Debug.Level > 0) + { + Debug.Console(1, this, "Directory Results:\n"); + + foreach (DirectoryItem item in directory.DirectoryResults) + { + if (item is DirectoryFolder) + { + Debug.Console(1, this, "+ {0}", item.Name); + } + else if (item is DirectoryContact) + { + Debug.Console(1, this, "{0}", item.Name); + } + } + } + } + + public override void Dial(string s) + { + SendText(string.Format("xCommand Dial Number: \"{0}\"", s)); + } + + public void DialBookingId(string s) + { + SendText(string.Format("xCommand Dial BookingId: {0}", s)); + } + + public override void EndCall(CodecActiveCallItem activeCall) + { + SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id)); + } + + public override void EndAllCalls() + { + foreach (CodecActiveCallItem activeCall in ActiveCalls) + { + SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id)); + } + } + + public override void AcceptCall(CodecActiveCallItem item) + { + SendText("xCommand Call Accept"); + } + + public override void RejectCall(CodecActiveCallItem item) + { + SendText("xCommand Call Reject"); + } + + public override void SendDtmf(string s) + { + if (CallFavorites != null) + { + SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s)); + } + } + + public void SelectPresentationSource(int source) + { + PresentationSource = source; + + StartSharing(); + } + + /// + /// Select source 1 as the presetnation source + /// + public void SelectPresentationSource1() + { + SelectPresentationSource(1); + } + + /// + /// Select source 2 as the presetnation source + /// + public void SelectPresentationSource2() + { + SelectPresentationSource(2); + } + + public override void StartSharing() + { + string sendingMode = string.Empty; + + if (IsInCall) + sendingMode = "LocalRemote"; + else + sendingMode = "LocalOnly"; + + SendText(string.Format("xCommand Presentation Start PresentationSource: {0}", PresentationSource)); + } + + public override void StopSharing() + { + SendText(string.Format("xCommand Presentation Stop PresentationSource: {0}", PresentationSource)); + } + + public override void PrivacyModeOn() + { + SendText("xCommand Audio Microphones Mute"); + } + + public override void PrivacyModeOff() + { + SendText("xCommand Audio Microphones Unmute"); + } + + public override void PrivacyModeToggle() + { + SendText("xCommand Audio Microphones ToggleMute"); + } + + public override void MuteOff() + { + SendText("xCommand Audio Volume Unmute"); + } + + public override void MuteOn() + { + SendText("xCommand Audio Volume Mute"); + } + + public override void MuteToggle() + { + SendText("xCommand Audio Volume ToggleMute"); + } + + /// + /// Increments the voluem + /// + /// + public override void VolumeUp(bool pressRelease) + { + SendText("xCommand Audio Volume Increase"); + } + + /// + /// Decrements the volume + /// + /// + public override void VolumeDown(bool pressRelease) + { + SendText("xCommand Audio Volume Decrease"); + } + + /// + /// Scales the level and sets the codec to the specified level within its range + /// + /// level from slider (0-65535 range) + public override void SetVolume(ushort level) + { + var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0); + SendText(string.Format("xCommand Audio Volume Set Level: {0}", scaledLevel)); + } + + /// + /// Recalls the default volume on the codec + /// + public void VolumeSetToDefault() + { + SendText("xCommand Audio Volume SetToDefault"); + } + + /// + /// Puts the codec in standby mode + /// + public void StandbyActivate() + { + SendText("xCommand Standby Activate"); + } + + /// + /// Wakes the codec from standby + /// + public void StandbyDeactivate() + { + SendText("xCommand Standby Deactivate"); + } + + /// + /// Reboots the codec + /// + public void Reboot() + { + SendText("xCommand SystemUnit Boot Action: Restart"); + } + + /// + /// Turns on Selfview Mode + /// + public void SelfviewModeOn() + { + SendText("xCommand Video Selfview Set Mode: On"); + } + + /// + /// Turns off Selfview Mode + /// + public void SelfviewModeOff() + { + SendText("xCommand Video Selfview Set Mode: Off"); + } + + /// + /// Toggles Selfview mode on/off + /// + public void SelfviewModeToggle() + { + string mode = string.Empty; + + if (CodecStatus.Status.Video.Selfview.Mode.BoolValue) + mode = "Off"; + else + mode = "On"; + + SendText(string.Format("xCommand Video Selfview Set Mode: {0}", mode)); + } + + /// + /// Sets a specified position for the selfview PIP window + /// + /// + public void SelfviewPipPositionSet(CodecCommandWithLabel position) + { + SendText(string.Format("xCommand Video Selfview Set Mode: On PIPPosition: {0}", position.Command)); + } + + /// + /// Toggles to the next selfview PIP position + /// + public void SelfviewPipPositionToggle() + { + if (CurrentSelfviewPipPosition != null) + { + var nextPipPositionIndex = SelfviewPipPositions.IndexOf(CurrentSelfviewPipPosition) + 1; + + if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list + nextPipPositionIndex = 0; + + SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]); + } + } + + /// + /// Sets a specific local layout + /// + /// + public void LocalLayoutSet(CodecCommandWithLabel layout) + { + SendText(string.Format("xCommand Video Layout LayoutFamily Set Target: local LayoutFamily: {0}", layout.Command)); + } + + /// + /// Toggles to the next local layout + /// + public void LocalLayoutToggle() + { + if(CurrentLocalLayout != null) + { + var nextLocalLayoutIndex = LocalLayouts.IndexOf(CurrentLocalLayout) + 1; + + if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list + nextLocalLayoutIndex = 0; + + LocalLayoutSet(LocalLayouts[nextLocalLayoutIndex]); + } + } + + /// + /// Calculates the current selfview PIP position + /// + void ComputeSelfviewPipStatus() + { + CurrentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower())); + + if(CurrentSelfviewPipPosition != null) + SelfviewIsOnFeedback.FireUpdate(); + } + + /// + /// Calculates the current local Layout + /// + void ComputeLocalLayout() + { + CurrentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower())); + + if (CurrentLocalLayout != null) + LocalLayoutFeedback.FireUpdate(); + } + + public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry) + { + SendText(string.Format("xCommand CallHistory DeleteEntry CallHistoryId: {0} AcknowledgeConsecutiveDuplicates: True", entry.OccurrenceHistoryId)); + } + + public class CiscoCodecInfo : VideoCodecInfo + { + public CiscoCodecStatus.RootObject CodecStatus { get; private set; } + + public CiscoCodecConfiguration.RootObject CodecConfiguration { get; private set; } + + public override bool MultiSiteOptionIsEnabled + { + get + { + if (CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value.ToLower() == "true") + return true; + else + return false; + } + + } + public override string IpAddress + { + get + { + if (CodecConfiguration.Configuration.Network != null) + { + if (CodecConfiguration.Configuration.Network.Count > 0) + return CodecConfiguration.Configuration.Network[0].IPv4.Address.Value; + } + return string.Empty; + } + } + public override string PhoneNumber + { + get + { + if (CodecConfiguration.Configuration.H323.H323Alias.E164 != null) + return CodecConfiguration.Configuration.H323.H323Alias.E164.Value; + else + return string.Empty; + } + } + public override string SipUri + { + get + { + if (CodecConfiguration.Configuration.H323.H323Alias.ID != null) + return CodecConfiguration.Configuration.H323.H323Alias.ID.Value; + else + return string.Empty; + } + } + public override bool AutoAnswerEnabled + { + get + { + if (CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value.ToLower() == "on") + return true; + else + return false; + } + } + + public CiscoCodecInfo(CiscoCodecStatus.RootObject status, CiscoCodecConfiguration.RootObject configuration) + { + CodecStatus = status; + CodecConfiguration = configuration; + } + } + } + + /// + /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes + /// + public class CodecCommandWithLabel + { + public string Command { get; set; } + public string Label { get; set; } + + public CodecCommandWithLabel(string command, string label) + { + Command = command; + Label = label; + } + } + + /// + /// Tracks the initial sycnronization state of the codec when making a connection + /// + public class CodecSyncState : IKeyed + { + bool _InitialSyncComplete; + + public event EventHandler InitialSyncCompleted; + + public string Key { get; private set; } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + handler(this, new EventArgs()); + } + _InitialSyncComplete = value; + } + } + + public bool InitialStatusMessageWasReceived { get; private set; } + + public bool InitialConfigurationMessageWasReceived { get; private set; } + + public bool FeedbackWasRegistered { get; private set; } + + public CodecSyncState(string key) + { + Key = key; + CodecDisconnected(); + } + + public void InitialStatusMessageReceived() + { + InitialStatusMessageWasReceived = true; + Debug.Console(1, this, "Initial Codec Status Message Received."); + CheckSyncStatus(); + } + + public void InitialConfigurationMessageReceived() + { + InitialConfigurationMessageWasReceived = true; + Debug.Console(1, this, "Initial Codec Configuration Message Received."); + CheckSyncStatus(); + } + + public void FeedbackRegistered() + { + FeedbackWasRegistered = true; + Debug.Console(1, this, "Initial Codec Feedback Registration Successful."); + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + InitialConfigurationMessageWasReceived = false; + InitialStatusMessageWasReceived = false; + FeedbackWasRegistered = false; + InitialSyncComplete = false; + } + + void CheckSyncStatus() + { + if (InitialConfigurationMessageWasReceived && InitialStatusMessageWasReceived && FeedbackWasRegistered) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Codec Sync Complete!"); + } + else + InitialSyncComplete = false; + } + } + + /// + /// Used to track the status of syncronizing the phonebook values when connecting to a codec or refreshing the phonebook info + /// + public class CodecPhonebookSyncState : IKeyed + { + bool _InitialSyncComplete; + + public event EventHandler InitialSyncCompleted; + + public string Key { get; private set; } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + handler(this, new EventArgs()); + } + _InitialSyncComplete = value; + } + } + + public bool InitialPhonebookFoldersWasReceived { get; private set; } + + public bool NumberOfContactsWasReceived { get; private set; } + + public bool PhonebookRootEntriesWasRecieved { get; private set; } + + public bool PhonebookHasFolders { get; private set; } + + public int NumberOfContacts { get; private set; } + + public CodecPhonebookSyncState(string key) + { + Key = key; + + CodecDisconnected(); + } + + public void InitialPhonebookFoldersReceived() + { + InitialPhonebookFoldersWasReceived = true; + + CheckSyncStatus(); + } + + public void PhonebookRootEntriesReceived() + { + PhonebookRootEntriesWasRecieved = true; + + CheckSyncStatus(); + } + + public void SetPhonebookHasFolders(bool value) + { + PhonebookHasFolders = value; + + Debug.Console(1, this, "Phonebook has folders: {0}", PhonebookHasFolders); + } + + public void SetNumberOfContacts(int contacts) + { + NumberOfContacts = contacts; + NumberOfContactsWasReceived = true; + + Debug.Console(1, this, "Phonebook contains {0} contacts.", NumberOfContacts); + + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + InitialPhonebookFoldersWasReceived = false; + PhonebookHasFolders = false; + NumberOfContacts = 0; + NumberOfContactsWasReceived = false; + } + + void CheckSyncStatus() + { + if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Phonebook Sync Complete!"); + } + else + InitialSyncComplete = false; + } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs new file mode 100644 index 00000000..38344f18 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs @@ -0,0 +1,1846 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Cameras; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.Occupancy; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration }; + + public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, + IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView, + ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets + { + public event EventHandler DirectoryResultReturned; + + public CommunicationGather PortGather { get; private set; } + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + public BoolFeedback PresentationViewMaximizedFeedback { get; private set; } + + string CurrentPresentationView; + + public BoolFeedback RoomIsOccupiedFeedback { get; private set; } + + public IntFeedback PeopleCountFeedback { get; private set; } + + public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; } + + public BoolFeedback SelfviewIsOnFeedback { get; private set; } + + public StringFeedback SelfviewPipPositionFeedback { get; private set; } + + public StringFeedback LocalLayoutFeedback { get; private set; } + + public BoolFeedback LocalLayoutIsProminentFeedback { get; private set; } + + public BoolFeedback FarEndIsSharingContentFeedback { get; private set; } + + private CodecCommandWithLabel CurrentSelfviewPipPosition; + + private CodecCommandWithLabel CurrentLocalLayout; + + /// + /// List the available positions for the selfview PIP window + /// + public List SelfviewPipPositions = new List() + { + new CodecCommandWithLabel("CenterLeft", "Center Left"), + new CodecCommandWithLabel("CenterRight", "Center Right"), + new CodecCommandWithLabel("LowerLeft", "Lower Left"), + new CodecCommandWithLabel("LowerRight", "Lower Right"), + new CodecCommandWithLabel("UpperCenter", "Upper Center"), + new CodecCommandWithLabel("UpperLeft", "Upper Left"), + new CodecCommandWithLabel("UpperRight", "Upper Right"), + }; + + /// + /// Lists the available options for local layout + /// + public List LocalLayouts = new List() + { + //new CodecCommandWithLabel("auto", "Auto"), + //new CiscoCodecLocalLayout("custom", "Custom"), // Left out for now + new CodecCommandWithLabel("equal","Equal"), + new CodecCommandWithLabel("overlay","Overlay"), + new CodecCommandWithLabel("prominent","Prominent"), + new CodecCommandWithLabel("single","Single") + }; + + private CiscoCodecConfiguration.RootObject CodecConfiguration = new CiscoCodecConfiguration.RootObject(); + + private CiscoCodecStatus.RootObject CodecStatus = new CiscoCodecStatus.RootObject(); + + public CodecCallHistory CallHistory { get; private set; } + + public CodecCallFavorites CallFavorites { get; private set; } + + /// + /// The root level of the directory + /// + public CodecDirectory DirectoryRoot { get; private set; } + + /// + /// Represents the current state of the directory and is computed on get + /// + public CodecDirectory CurrentDirectoryResult + { + get + { + if (DirectoryBrowseHistory.Count > 0) + return DirectoryBrowseHistory[DirectoryBrowseHistory.Count - 1]; + else + return DirectoryRoot; + } + } + + public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } + + /// + /// Tracks the directory browse history when browsing beyond the root directory + /// + public List DirectoryBrowseHistory { get; private set; } + + public CodecScheduleAwareness CodecSchedule { get; private set; } + + /// + /// Gets and returns the scaled volume of the codec + /// + protected override Func VolumeLevelFeedbackFunc + { + get + { + return () => CrestronEnvironment.ScaleWithLimits(CodecStatus.Status.Audio.Volume.IntValue, 100, 0, 65535, 0); + } + } + + protected override Func PrivacyModeIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Audio.Microphones.Mute.BoolValue; + } + } + + protected override Func StandbyIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Standby.State.BoolValue; + } + } + + /// + /// Gets the value of the currently shared source, or returns null + /// + protected override Func SharingSourceFeedbackFunc + { + get + { + return () => PresentationSourceKey; + } + } + + protected override Func SharingContentIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Conference.Presentation.Mode.BoolValue; + } + } + + protected Func FarEndIsSharingContentFeedbackFunc + { + get + { + return () => CodecStatus.Status.Conference.Presentation.Mode.Value == "Receiving"; + } + } + + protected override Func MuteFeedbackFunc + { + get + { + return () => CodecStatus.Status.Audio.VolumeMute.BoolValue; + } + } + + protected Func RoomIsOccupiedFeedbackFunc + { + get + { + return () => CodecStatus.Status.RoomAnalytics.PeoplePresence.BoolValue; + } + } + + protected Func PeopleCountFeedbackFunc + { + get + { + return () => CodecStatus.Status.RoomAnalytics.PeopleCount.Current.IntValue; + } + } + + protected Func SpeakerTrackIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Cameras.SpeakerTrack.Status.BoolValue; + } + } + + protected Func SelfViewIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Video.Selfview.Mode.BoolValue; + } + } + + protected Func SelfviewPipPositionFeedbackFunc + { + get + { + return () => CurrentSelfviewPipPosition.Label; + } + } + + protected Func LocalLayoutFeedbackFunc + { + get + { + return () => CurrentLocalLayout.Label; + } + } + + protected Func LocalLayoutIsProminentFeedbackFunc + { + get + { + return () => CurrentLocalLayout.Label == "Prominent"; + } + } + + + private string CliFeedbackRegistrationExpression; + + private CodecSyncState SyncState; + + public CodecPhonebookSyncState PhonebookSyncState { get; private set; } + + private StringBuilder JsonMessage; + + private bool JsonFeedbackMessageIsIncoming; + + public bool CommDebuggingIsOn; + + string Delimiter = "\r\n"; + + /// + /// Used to track the current connector used for the presentation source + /// + int PresentationSource; + + string PresentationSourceKey; + + string PhonebookMode = "Local"; // Default to Local + + int PhonebookResultsLimit = 255; // Could be set later by config. + + CTimer LoginMessageReceivedTimer; + CTimer RetryConnectionTimer; + + // **___________________________________________________________________** + // Timers to be moved to the global system timer at a later point.... + CTimer BookingsRefreshTimer; + CTimer PhonebookRefreshTimer; + // **___________________________________________________________________** + + public RoutingInputPort CodecOsdIn { get; private set; } + public RoutingInputPort HdmiIn2 { get; private set; } + public RoutingInputPort HdmiIn3 { get; private set; } + public RoutingOutputPort HdmiOut1 { get; private set; } + public RoutingOutputPort HdmiOut2 { get; private set; } + + + // Constructor for IBasicCommunication + public CiscoSparkCodec(DeviceConfig config, IBasicCommunication comm) + : base(config) + { + + + var props = JsonConvert.DeserializeObject(config.Properties.ToString()); + + RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); + PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc); + CameraAutoModeIsOnFeedback = new BoolFeedback(SpeakerTrackIsOnFeedbackFunc); + SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); + SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); + LocalLayoutFeedback = new StringFeedback(LocalLayoutFeedbackFunc); + LocalLayoutIsProminentFeedback = new BoolFeedback(LocalLayoutIsProminentFeedbackFunc); + FarEndIsSharingContentFeedback = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); + + PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized"); + + Communication = comm; + + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "xStatus SystemUnit Software Version\r"); + } + + if (props.Sharing != null) + AutoShareContentWhileInCall = props.Sharing.AutoShareContentWhileInCall; + + ShowSelfViewByDefault = props.ShowSelfViewByDefault; + + DeviceManager.AddDevice(CommunicationMonitor); + + PhonebookMode = props.PhonebookMode; + + SyncState = new CodecSyncState(Key + "--Sync"); + + PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); + + SyncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted); + + PortGather = new CommunicationGather(Communication, Delimiter); + PortGather.IncludeDelimiter = true; + PortGather.LineReceived += this.Port_LineReceived; + + CodecInfo = new CiscoCodecInfo(CodecStatus, CodecConfiguration); + + CallHistory = new CodecCallHistory(); + + if (props.Favorites != null) + { + CallFavorites = new CodecCallFavorites(); + CallFavorites.Favorites = props.Favorites; + } + + DirectoryRoot = new CodecDirectory(); + + DirectoryBrowseHistory = new List(); + + CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => DirectoryBrowseHistory.Count > 0); + + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + + CodecSchedule = new CodecScheduleAwareness(); + + //Set Feedback Actions + CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate; + CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate; + CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate; + CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate; + CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate; + CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate; + CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = CameraAutoModeIsOnFeedback.FireUpdate; + CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate; + CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus; + CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout; + CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction += SharingContentIsOnFeedback.FireUpdate; + CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction += FarEndIsSharingContentFeedback.FireUpdate; + + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); + HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this); + HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this); + + HdmiOut1 = new RoutingOutputPort(RoutingPortNames.HdmiOut1, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + HdmiOut2 = new RoutingOutputPort(RoutingPortNames.HdmiOut2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + InputPorts.Add(CodecOsdIn); + InputPorts.Add(HdmiIn2); + InputPorts.Add(HdmiIn3); + OutputPorts.Add(HdmiOut1); + + SetUpCameras(); + + CreateOsdSource(); + } + + + /// + /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input + /// to enable routing + /// + void CreateOsdSource() + { + OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); + DeviceManager.AddDevice(OsdSource); + var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); + TieLineCollection.Default.Add(tl); + } + + /// + /// Starts the HTTP feedback server and syncronizes state of codec + /// + /// + public override bool CustomActivate() + { + CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); + + var socket = Communication as ISocketStatus; + if (socket != null) + { + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + + Communication.Connect(); + + CommunicationMonitor.Start(); + + string prefix = "xFeedback register "; + + CliFeedbackRegistrationExpression = + prefix + "/Configuration" + Delimiter + + prefix + "/Status/Audio" + Delimiter + + prefix + "/Status/Call" + Delimiter + + prefix + "/Status/Conference/Presentation" + Delimiter + + prefix + "/Status/Cameras/SpeakerTrack" + Delimiter + + prefix + "/Status/RoomAnalytics" + Delimiter + + prefix + "/Status/RoomPreset" + Delimiter + + prefix + "/Status/Standby" + Delimiter + + prefix + "/Status/Video/Selfview" + Delimiter + + prefix + "/Status/Video/Layout" + Delimiter + + prefix + "/Bookings" + Delimiter + + prefix + "/Event/CallDisconnect" + Delimiter + + prefix + "/Event/Bookings" + Delimiter + + prefix + "/Event/CameraPresetListUpdated" + Delimiter; + + return base.CustomActivate(); + } + + /// + /// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc. + /// + /// + /// + void SyncState_InitialSyncCompleted(object sender, EventArgs e) + { + // Fire the ready event + SetIsReady(); + //CommDebuggingIsOn = false; + + GetCallHistory(); + + PhonebookRefreshTimer = new CTimer(CheckCurrentHour, 3600000, 3600000); // check each hour to see if the phonebook should be downloaded + GetPhonebook(null); + + BookingsRefreshTimer = new CTimer(GetBookings, 900000, 900000); // 15 minute timer to check for new booking info + GetBookings(null); + } + + public void SetCommDebug(string s) + { + if (s == "1") + { + CommDebuggingIsOn = true; + Debug.Console(0, this, "Comm Debug Enabled."); + } + else + { + CommDebuggingIsOn = false; + Debug.Console(0, this, "Comm Debug Disabled."); + } + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus); + if (e.Client.IsConnected) + { + if(!SyncState.LoginMessageWasReceived) + LoginMessageReceivedTimer = new CTimer(o => DisconnectClientAndReconnect(), 5000); + } + else + { + SyncState.CodecDisconnected(); + PhonebookSyncState.CodecDisconnected(); + + if (PhonebookRefreshTimer != null) + { + PhonebookRefreshTimer.Stop(); + PhonebookRefreshTimer = null; + } + + if (BookingsRefreshTimer != null) + { + BookingsRefreshTimer.Stop(); + BookingsRefreshTimer = null; + } + } + } + + void DisconnectClientAndReconnect() + { + Debug.Console(1, this, "Retrying connection to codec."); + + Communication.Disconnect(); + + RetryConnectionTimer = new CTimer(o => Communication.Connect(), 2000); + + //CrestronEnvironment.Sleep(2000); + + //Communication.Connect(); + } + + /// + /// Gathers responses from the codec (including the delimiter. Responses are checked to see if they contain JSON data and if so, the data is collected until a complete JSON + /// message is received before forwarding the message to be deserialized. + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + if (CommDebuggingIsOn) + { + if(!JsonFeedbackMessageIsIncoming) + Debug.Console(1, this, "RX: '{0}'", args.Text); + } + + if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message + { + JsonFeedbackMessageIsIncoming = true; + + if (CommDebuggingIsOn) + Debug.Console(1, this, "Incoming JSON message..."); + + JsonMessage = new StringBuilder(); + } + else if (args.Text == "}" + Delimiter) // Check for the end of a JSON message + { + JsonFeedbackMessageIsIncoming = false; + + JsonMessage.Append(args.Text); + + if (CommDebuggingIsOn) + Debug.Console(1, this, "Complete JSON Received:\n{0}", JsonMessage.ToString()); + + // Forward the complete message to be deserialized + DeserializeResponse(JsonMessage.ToString()); + return; + } + + if(JsonFeedbackMessageIsIncoming) + { + JsonMessage.Append(args.Text); + + //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); + return; + } + + if (!SyncState.InitialSyncComplete) + { + switch (args.Text.Trim().ToLower()) // remove the whitespace + { + case "*r login successful": + { + SyncState.LoginMessageReceived(); + + if(LoginMessageReceivedTimer != null) + LoginMessageReceivedTimer.Stop(); + + SendText("xPreferences outputmode json"); + break; + } + case "xpreferences outputmode json": + { + if (!SyncState.InitialStatusMessageWasReceived) + SendText("xStatus"); + break; + } + case "xfeedback register /event/calldisconnect": + { + SyncState.FeedbackRegistered(); + break; + } + } + } + + } + + public void SendText(string command) + { + if (CommDebuggingIsOn) + Debug.Console(1, this, "Sending: '{0}'", command); + + Communication.SendText(command + Delimiter); + } + + void DeserializeResponse(string response) + { + try + { + //// Serializer settings. We want to ignore null values and missing members + //JsonSerializerSettings settings = new JsonSerializerSettings(); + //settings.NullValueHandling = NullValueHandling.Ignore; + //settings.MissingMemberHandling = MissingMemberHandling.Ignore; + //settings.ObjectCreationHandling = ObjectCreationHandling.Auto; + + if (response.IndexOf("\"Status\":{") > -1) + { + // Status Message + + // Temp object so we can inpsect for call data before simply deserializing + CiscoCodecStatus.RootObject tempCodecStatus = new CiscoCodecStatus.RootObject(); + + JsonConvert.PopulateObject(response, tempCodecStatus); + + // Check to see if the message contains /Status/Conference/Presentation/LocalInstance and extract source value + var conference = tempCodecStatus.Status.Conference; + + if (conference.Presentation.LocalInstance.Count > 0) + { + if (!string.IsNullOrEmpty(conference.Presentation.LocalInstance[0].ghost)) + PresentationSource = 0; + else if (conference.Presentation.LocalInstance[0].Source != null) + { + PresentationSource = conference.Presentation.LocalInstance[0].Source.IntValue; + } + } + + // Check to see if this is a call status message received after the initial status message + if (tempCodecStatus.Status.Call.Count > 0) + { + // Iterate through the call objects in the response + foreach (CiscoCodecStatus.Call call in tempCodecStatus.Status.Call) + { + var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(call.id)); + + if (tempActiveCall != null) + { + bool changeDetected = false; + + eCodecCallStatus newStatus = eCodecCallStatus.Unknown; + + // Update properties of ActiveCallItem + if(call.Status != null) + if (!string.IsNullOrEmpty(call.Status.Value)) + { + tempActiveCall.Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value); + + if (newStatus == eCodecCallStatus.Connected) + GetCallHistory(); + + changeDetected = true; + } + if (call.CallType != null) + if (!string.IsNullOrEmpty(call.CallType.Value)) + { + tempActiveCall.Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value); + changeDetected = true; + } + if (call.DisplayName != null) + if (!string.IsNullOrEmpty(call.DisplayName.Value)) + { + tempActiveCall.Name = call.DisplayName.Value; + changeDetected = true; + } + if (call.Direction != null) + { + if (!string.IsNullOrEmpty(call.Direction.Value)) + { + tempActiveCall.Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value); + changeDetected = true; + } + } + + if (changeDetected) + { + SetSelfViewMode(); + OnCallStatusChange(tempActiveCall); + ListCalls(); + } + } + else if( call.ghost == null ) // if the ghost value is present the call has ended already + { + // Create a new call item + var newCallItem = new CodecActiveCallItem() + { + Id = call.id, + Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value), + Name = call.DisplayName.Value, + Number = call.RemoteNumber.Value, + Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value), + Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value) + }; + + // Add it to the ActiveCalls List + ActiveCalls.Add(newCallItem); + + ListCalls(); + + SetSelfViewMode(); + OnCallStatusChange(newCallItem); + } + + } + + } + + // Check for Room Preset data (comes in partial, so we need to handle these responses differently to prevent appending duplicate items + var tempPresets = tempCodecStatus.Status.RoomPreset; + + if (tempPresets.Count > 0) + { + // Create temporary list to store the existing items from the CiscoCodecStatus.RoomPreset collection + List existingRoomPresets = new List(); + // Add the existing items to the temporary list + existingRoomPresets.AddRange(CodecStatus.Status.RoomPreset); + // Populate the CodecStatus object (this will append new values to the RoomPreset collection + JsonConvert.PopulateObject(response, CodecStatus); + + JObject jResponse = JObject.Parse(response); + + IList roomPresets = jResponse["Status"]["RoomPreset"].Children().ToList(); + // Iterate the new items in this response agains the temporary list. Overwrite any existing items and add new ones. + foreach (var preset in tempPresets) + { + // First fine the existing preset that matches the id + var existingPreset = existingRoomPresets.FirstOrDefault(p => p.id.Equals(preset.id)); + if (existingPreset != null) + { + Debug.Console(1, this, "Existing Room Preset with ID: {0} found. Updating.", existingPreset.id); + + JToken updatedPreset = null; + + // Find the JToken from the response with the matching id + foreach (var jPreset in roomPresets) + { + if (jPreset["id"].Value() == existingPreset.id) + updatedPreset = jPreset; + } + + if (updatedPreset != null) + { + // use PopulateObject to overlay the partial data onto the existing object + JsonConvert.PopulateObject(updatedPreset.ToString(), existingPreset); + } + + } + else + { + Debug.Console(1, this, "New Room Preset with ID: {0}. Adding.", preset.id); + existingRoomPresets.Add(preset); + } + } + + // Replace the list in the CodecStatus object with the processed list + CodecStatus.Status.RoomPreset = existingRoomPresets; + + // Generecise the list + NearEndPresets = RoomPresets.GetGenericPresets(CodecStatus.Status.RoomPreset); + + var handler = CodecRoomPresetsListHasChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + else + { + JsonConvert.PopulateObject(response, CodecStatus); + } + + if (!SyncState.InitialStatusMessageWasReceived) + { + SyncState.InitialStatusMessageReceived(); + + if (!SyncState.InitialConfigurationMessageWasReceived) + SendText("xConfiguration"); + } + } + else if (response.IndexOf("\"Configuration\":{") > -1) + { + // Configuration Message + + JsonConvert.PopulateObject(response, CodecConfiguration); + + if (!SyncState.InitialConfigurationMessageWasReceived) + { + SyncState.InitialConfigurationMessageReceived(); + if (!SyncState.FeedbackWasRegistered) + { + SendText(CliFeedbackRegistrationExpression); + } + } + + } + else if (response.IndexOf("\"Event\":{") > -1) + { + if (response.IndexOf("\"CallDisconnect\":{") > -1) + { + CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject(); + + JsonConvert.PopulateObject(response, eventReceived); + + EvalutateDisconnectEvent(eventReceived); + } + else if (response.IndexOf("\"Bookings\":{") > -1) // The list has changed, reload it + { + GetBookings(null); + } + } + else if (response.IndexOf("\"CommandResponse\":{") > -1) + { + // CommandResponse Message + + if (response.IndexOf("\"CallHistoryRecentsResult\":{") > -1) + { + var codecCallHistory = new CiscoCallHistory.RootObject(); + + JsonConvert.PopulateObject(response, codecCallHistory); + + CallHistory.ConvertCiscoCallHistoryToGeneric(codecCallHistory.CommandResponse.CallHistoryRecentsResult.Entry); + } + else if (response.IndexOf("\"CallHistoryDeleteEntryResult\":{") > -1) + { + GetCallHistory(); + } + else if (response.IndexOf("\"PhonebookSearchResult\":{") > -1) + { + var codecPhonebookResponse = new CiscoCodecPhonebook.RootObject(); + + JsonConvert.PopulateObject(response, codecPhonebookResponse); + + if (!PhonebookSyncState.InitialPhonebookFoldersWasReceived) + { + // Check if the phonebook has any folders + PhonebookSyncState.InitialPhonebookFoldersReceived(); + + PhonebookSyncState.SetPhonebookHasFolders(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.Folder.Count > 0); + + if (PhonebookSyncState.PhonebookHasFolders) + { + DirectoryRoot.AddFoldersToDirectory(CiscoCodecPhonebook.GetRootFoldersFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); + } + + // Get the number of contacts in the phonebook + GetPhonebookContacts(); + } + else if (!PhonebookSyncState.NumberOfContactsWasReceived) + { + // Store the total number of contacts in the phonebook + PhonebookSyncState.SetNumberOfContacts(Int32.Parse(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value)); + + DirectoryRoot.AddContactsToDirectory(CiscoCodecPhonebook.GetRootContactsFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); + + PhonebookSyncState.PhonebookRootEntriesReceived(); + + PrintDirectory(DirectoryRoot); + } + else if (PhonebookSyncState.InitialSyncComplete) + { + var directoryResults = new CodecDirectory(); + + if(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value != "0") + directoryResults = CiscoCodecPhonebook.ConvertCiscoPhonebookToGeneric(codecPhonebookResponse.CommandResponse.PhonebookSearchResult); + + PrintDirectory(directoryResults); + + DirectoryBrowseHistory.Add(directoryResults); + + OnDirectoryResultReturned(directoryResults); + + } + } + else if (response.IndexOf("\"BookingsListResult\":{") > -1) + { + var codecBookings = new CiscoCodecBookings.RootObject(); + + JsonConvert.PopulateObject(response, codecBookings); + + if(codecBookings.CommandResponse.BookingsListResult.ResultInfo.TotalRows.Value != "0") + CodecSchedule.Meetings = CiscoCodecBookings.GetGenericMeetingsFromBookingResult(codecBookings.CommandResponse.BookingsListResult.Booking); + + BookingsRefreshTimer.Reset(900000, 900000); + } + + } + + } + catch (Exception ex) + { + Debug.Console(1, this, "Error Deserializing feedback from codec: {0}", ex); + } + } + + /// + /// Call when directory results are updated + /// + /// + void OnDirectoryResultReturned(CodecDirectory result) + { + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + + // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology + var handler = DirectoryResultReturned; + if (handler != null) + { + handler(this, new DirectoryEventArgs() + { + Directory = result, + DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue + }); + } + + PrintDirectory(result); + } + + /// + /// Evaluates an event received from the codec + /// + /// + void EvalutateDisconnectEvent(CiscoCodecEvents.RootObject eventReceived) + { + if (eventReceived.Event.CallDisconnect != null) + { + var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(eventReceived.Event.CallDisconnect.CallId.Value)); + + // Remove the call from the Active calls list + if (tempActiveCall != null) + { + ActiveCalls.Remove(tempActiveCall); + + ListCalls(); + + SetSelfViewMode(); + // Notify of the call disconnection + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, tempActiveCall); + + GetCallHistory(); + } + } + } + + /// + /// + /// + /// + public override void ExecuteSwitch(object selector) + { + (selector as Action)(); + PresentationSourceKey = selector.ToString(); + } + + /// + /// This is necessary for devices that are "routers" in the middle of the path, even though it only has one output and + /// may only have one input. + /// + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + ExecuteSwitch(inputSelector); + PresentationSourceKey = inputSelector.ToString(); + } + + + /// + /// Gets the ID of the last connected call + /// + /// + public string GetCallId() + { + string callId = null; + + if (ActiveCalls.Count > 1) + { + var lastCallIndex = ActiveCalls.Count - 1; + callId = ActiveCalls[lastCallIndex].Id; + } + else if (ActiveCalls.Count == 1) + callId = ActiveCalls[0].Id; + + return callId; + + } + + public void GetCallHistory() + { + SendText("xCommand CallHistory Recents Limit: 20 Order: OccurrenceTime"); + } + + /// + /// Required for IHasScheduleAwareness + /// + public void GetSchedule() + { + GetBookings(null); + } + + /// + /// Gets the bookings for today + /// + /// + public void GetBookings(object command) + { + Debug.Console(1, this, "Retrieving Booking Info from Codec. Current Time: {0}", DateTime.Now.ToLocalTime()); + + SendText("xCommand Bookings List Days: 1 DayOffset: 0"); + } + + /// + /// Checks to see if it is 2am (or within that hour) and triggers a download of the phonebook + /// + /// + public void CheckCurrentHour(object o) + { + if (DateTime.Now.Hour == 2) + { + Debug.Console(1, this, "Checking hour to see if phonebook should be downloaded. Current hour is {0}", DateTime.Now.Hour); + + GetPhonebook(null); + PhonebookRefreshTimer.Reset(3600000, 3600000); + } + } + + /// + /// Triggers a refresh of the codec phonebook + /// + /// Just to allow this method to be called from a console command + public void GetPhonebook(string command) + { + PhonebookSyncState.CodecDisconnected(); + + DirectoryRoot = new CodecDirectory(); + + GetPhonebookFolders(); + } + + private void GetPhonebookFolders() + { + // Get Phonebook Folders (determine local/corporate from config, and set results limit) + SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", PhonebookMode)); + } + + private void GetPhonebookContacts() + { + // Get Phonebook Folders (determine local/corporate from config, and set results limit) + SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact Limit: {1}", PhonebookMode, PhonebookResultsLimit)); + } + + /// + /// Searches the codec phonebook for all contacts matching the search string + /// + /// + public void SearchDirectory(string searchString) + { + SendText(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, PhonebookMode, PhonebookResultsLimit)); + } + + /// + /// // Get contents of a specific folder in the phonebook + /// + /// + public void GetDirectoryFolderContents(string folderId) + { + SendText(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Any Limit: {2}", folderId, PhonebookMode, PhonebookResultsLimit)); + } + + /// + /// Sets the parent folder contents or the directory root as teh current directory and fires the event. Used to browse up a level + /// + /// + public void GetDirectoryParentFolderContents() + { + var currentDirectory = new CodecDirectory(); + + if (DirectoryBrowseHistory.Count > 0) + { + var lastItemIndex = DirectoryBrowseHistory.Count - 1; + var parentDirectoryContents = DirectoryBrowseHistory[lastItemIndex]; + + DirectoryBrowseHistory.Remove(DirectoryBrowseHistory[lastItemIndex]); + + currentDirectory = parentDirectoryContents; + + } + else + { + currentDirectory = DirectoryRoot; + } + + OnDirectoryResultReturned(currentDirectory); + } + + /// + /// Clears the session browse history and fires the event with the directory root + /// + public void SetCurrentDirectoryToRoot() + { + DirectoryBrowseHistory.Clear(); + + OnDirectoryResultReturned(DirectoryRoot); + } + + /// + /// Prints the directory to console + /// + /// + void PrintDirectory(CodecDirectory directory) + { + if (Debug.Level > 0) + { + Debug.Console(1, this, "Directory Results:\n"); + + foreach (DirectoryItem item in directory.CurrentDirectoryResults) + { + if (item is DirectoryFolder) + { + Debug.Console(1, this, "[+] {0}", item.Name); + } + else if (item is DirectoryContact) + { + Debug.Console(1, this, "{0}", item.Name); + } + } + Debug.Console(1, this, "Directory is on Root Level: {0}", !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); + } + + } + + /// + /// Simple dial method + /// + /// + public override void Dial(string number) + { + SendText(string.Format("xCommand Dial Number: \"{0}\"", number)); + } + + /// + /// Dials a specific meeting + /// + /// + public override void Dial(Meeting meeting) + { + foreach (Call c in meeting.Calls) + { + Dial(c.Number, c.Protocol, c.CallRate, c.CallType, meeting.Id); + } + } + + /// + /// Detailed dial method + /// + /// + /// + /// + /// + /// + public void Dial(string number, string protocol, string callRate, string callType, string meetingId) + { + SendText(string.Format("xCommand Dial Number: \"{0}\" Protocol: {1} CallRate: {2} CallType: {3} BookingId: {4}", number, protocol, callRate, callType, meetingId)); + } + + public override void EndCall(CodecActiveCallItem activeCall) + { + SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id)); + } + + public override void EndAllCalls() + { + foreach (CodecActiveCallItem activeCall in ActiveCalls) + { + SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id)); + } + } + + public override void AcceptCall(CodecActiveCallItem item) + { + SendText("xCommand Call Accept"); + } + + public override void RejectCall(CodecActiveCallItem item) + { + SendText("xCommand Call Reject"); + } + + public override void SendDtmf(string s) + { + if (CallFavorites != null) + { + SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s)); + } + } + + public void SelectPresentationSource(int source) + { + PresentationSource = source; + + StartSharing(); + } + + /// + /// Select source 1 as the presetnation source + /// + public void SelectPresentationSource1() + { + SelectPresentationSource(2); + } + + /// + /// Select source 2 as the presetnation source + /// + public void SelectPresentationSource2() + { + SelectPresentationSource(3); + } + + /// + /// Starts presentation sharing + /// + public override void StartSharing() + { + string sendingMode = string.Empty; + + if (IsInCall) + sendingMode = "LocalRemote"; + else + sendingMode = "LocalOnly"; + + if(PresentationSource > 0) + SendText(string.Format("xCommand Presentation Start PresentationSource: {0} SendingMode: {1}", PresentationSource, sendingMode)); + } + + /// + /// Stops sharing the current presentation + /// + public override void StopSharing() + { + PresentationSource = 0; + + SendText("xCommand Presentation Stop"); + } + + public override void PrivacyModeOn() + { + SendText("xCommand Audio Microphones Mute"); + } + + public override void PrivacyModeOff() + { + SendText("xCommand Audio Microphones Unmute"); + } + + public override void PrivacyModeToggle() + { + SendText("xCommand Audio Microphones ToggleMute"); + } + + public override void MuteOff() + { + SendText("xCommand Audio Volume Unmute"); + } + + public override void MuteOn() + { + SendText("xCommand Audio Volume Mute"); + } + + public override void MuteToggle() + { + SendText("xCommand Audio Volume ToggleMute"); + } + + /// + /// Increments the voluem + /// + /// + public override void VolumeUp(bool pressRelease) + { + SendText("xCommand Audio Volume Increase"); + } + + /// + /// Decrements the volume + /// + /// + public override void VolumeDown(bool pressRelease) + { + SendText("xCommand Audio Volume Decrease"); + } + + /// + /// Scales the level and sets the codec to the specified level within its range + /// + /// level from slider (0-65535 range) + public override void SetVolume(ushort level) + { + var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0); + SendText(string.Format("xCommand Audio Volume Set Level: {0}", scaledLevel)); + } + + /// + /// Recalls the default volume on the codec + /// + public void VolumeSetToDefault() + { + SendText("xCommand Audio Volume SetToDefault"); + } + + /// + /// Puts the codec in standby mode + /// + public override void StandbyActivate() + { + SendText("xCommand Standby Activate"); + } + + /// + /// Wakes the codec from standby + /// + public override void StandbyDeactivate() + { + SendText("xCommand Standby Deactivate"); + } + + /// + /// Reboots the codec + /// + public void Reboot() + { + SendText("xCommand SystemUnit Boot Action: Restart"); + } + + /// + /// Sets SelfView Mode based on config + /// + void SetSelfViewMode() + { + if (!IsInCall) + { + SelfViewModeOff(); + } + else + { + if (ShowSelfViewByDefault) + SelfViewModeOn(); + else + SelfViewModeOff(); + } + } + + /// + /// Turns on Selfview Mode + /// + public void SelfViewModeOn() + { + SendText("xCommand Video Selfview Set Mode: On"); + } + + /// + /// Turns off Selfview Mode + /// + public void SelfViewModeOff() + { + SendText("xCommand Video Selfview Set Mode: Off"); + } + + /// + /// Toggles Selfview mode on/off + /// + public void SelfViewModeToggle() + { + string mode = string.Empty; + + if (CodecStatus.Status.Video.Selfview.Mode.BoolValue) + mode = "Off"; + else + mode = "On"; + + SendText(string.Format("xCommand Video Selfview Set Mode: {0}", mode)); + } + + /// + /// Sets a specified position for the selfview PIP window + /// + /// + public void SelfviewPipPositionSet(CodecCommandWithLabel position) + { + SendText(string.Format("xCommand Video Selfview Set Mode: On PIPPosition: {0}", position.Command)); + } + + /// + /// Toggles to the next selfview PIP position + /// + public void SelfviewPipPositionToggle() + { + if (CurrentSelfviewPipPosition != null) + { + var nextPipPositionIndex = SelfviewPipPositions.IndexOf(CurrentSelfviewPipPosition) + 1; + + if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list + nextPipPositionIndex = 0; + + SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]); + } + } + + /// + /// Sets a specific local layout + /// + /// + public void LocalLayoutSet(CodecCommandWithLabel layout) + { + SendText(string.Format("xCommand Video Layout LayoutFamily Set Target: local LayoutFamily: {0}", layout.Command)); + } + + /// + /// Toggles to the next local layout + /// + public void LocalLayoutToggle() + { + if(CurrentLocalLayout != null) + { + var nextLocalLayoutIndex = LocalLayouts.IndexOf(CurrentLocalLayout) + 1; + + if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list + nextLocalLayoutIndex = 0; + + LocalLayoutSet(LocalLayouts[nextLocalLayoutIndex]); + } + } + + /// + /// Toggles between single/prominent layouts + /// + public void LocalLayoutToggleSingleProminent() + { + if (CurrentLocalLayout != null) + { + if (CurrentLocalLayout.Label != "Prominent") + LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Prominent"))); + else + LocalLayoutSet(LocalLayouts.FirstOrDefault(l => l.Label.Equals("Single"))); + } + + } + + /// + /// + /// + public void MinMaxLayoutToggle() + { + if (PresentationViewMaximizedFeedback.BoolValue) + CurrentPresentationView = "Minimized"; + else + CurrentPresentationView = "Maximized"; + + SendText(string.Format("xCommand Video PresentationView Set View: {0}", CurrentPresentationView)); + PresentationViewMaximizedFeedback.FireUpdate(); + } + + /// + /// Calculates the current selfview PIP position + /// + void ComputeSelfviewPipStatus() + { + CurrentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower())); + + if(CurrentSelfviewPipPosition != null) + SelfviewIsOnFeedback.FireUpdate(); + } + + /// + /// Calculates the current local Layout + /// + void ComputeLocalLayout() + { + CurrentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower())); + + if (CurrentLocalLayout != null) + LocalLayoutFeedback.FireUpdate(); + } + + public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry) + { + SendText(string.Format("xCommand CallHistory DeleteEntry CallHistoryId: {0} AcknowledgeConsecutiveDuplicates: True", entry.OccurrenceHistoryId)); + } + + #region IHasCameraSpeakerTrack + + public void CameraAutoModeToggle() + { + if (!CameraAutoModeIsOnFeedback.BoolValue) + { + SendText("xCommand Cameras SpeakerTrack Activate"); + } + else + { + SendText("xCommand Cameras SpeakerTrack Deactivate"); + } + } + + public void CameraAutoModeOn() + { + SendText("xCommand Cameras SpeakerTrack Activate"); + } + + public void CameraAutoModeOff() + { + SendText("xCommand Cameras SpeakerTrack Deactivate"); + } + + #endregion + + /// + /// Builds the cameras List. Could later be modified to build from config data + /// + void SetUpCameras() + { + // Add the internal camera + Cameras = new List(); + + var internalCamera = new CiscoSparkCamera(Key + "-camera1", "Near End", this, 1); + + if(CodecStatus.Status.Cameras.Camera.Count > 0) + internalCamera.SetCapabilites(CodecStatus.Status.Cameras.Camera[0].Capabilities.Options.Value); + else + // Somehow subscribe to the event on the Options.Value property and update when it changes. + + Cameras.Add(internalCamera); + + // Add the far end camera + var farEndCamera = new CiscoFarEndCamera(Key + "-cameraFar", "Far End", this); + Cameras.Add(farEndCamera); + + SelectedCameraFeedback = new StringFeedback(() => SelectedCamera.Key); + + ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera); + + DeviceManager.AddDevice(internalCamera); + DeviceManager.AddDevice(farEndCamera); + + NearEndPresets = new List(15); + + FarEndRoomPresets = new List(15); + + // Add the far end presets + for (int i = 1; i <= FarEndRoomPresets.Capacity; i++) + { + var label = string.Format("Far End Preset {0}", i); + FarEndRoomPresets.Add(new CodecRoomPreset(i, label, true, false)); + } + + SelectedCamera = internalCamera; ; // call the method to select the camera and ensure the feedbacks get updated. + } + + #region IHasCodecCameras Members + + public event EventHandler CameraSelected; + + public List Cameras { get; private set; } + + public StringFeedback SelectedCameraFeedback { get; private set; } + + private CameraBase _selectedCamera; + + /// + /// Returns the selected camera + /// + public CameraBase SelectedCamera + { + get + { + return _selectedCamera; + } + private set + { + _selectedCamera = value; + SelectedCameraFeedback.FireUpdate(); + ControllingFarEndCameraFeedback.FireUpdate(); + + var handler = CameraSelected; + if (handler != null) + { + handler(this, new CameraSelectedEventArgs(SelectedCamera)); + } + } + } + + public void SelectCamera(string key) + { + var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1); + if (camera != null) + { + Debug.Console(2, this, "Selected Camera with key: '{0}'", camera.Key); + SelectedCamera = camera; + } + else + Debug.Console(2, this, "Unable to select camera with key: '{0}'", key); + } + + public CameraBase FarEndCamera { get; private set; } + + public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } + + #endregion + + /// + /// + /// + public class CiscoCodecInfo : VideoCodecInfo + { + public CiscoCodecStatus.RootObject CodecStatus { get; private set; } + + public CiscoCodecConfiguration.RootObject CodecConfiguration { get; private set; } + + public override bool MultiSiteOptionIsEnabled + { + get + { + if (!string.IsNullOrEmpty(CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value) && CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value.ToLower() == "true") + return true; + else + return false; + } + + } + public override string IpAddress + { + get + { + if (CodecConfiguration.Configuration.Network != null) + { + if (CodecConfiguration.Configuration.Network.Count > 0) + return CodecConfiguration.Configuration.Network[0].IPv4.Address.Value; + } + return string.Empty; + } + } + public override string E164Alias + { + get + { + if (CodecConfiguration.Configuration.H323.H323Alias.E164 != null) + return CodecConfiguration.Configuration.H323.H323Alias.E164.Value; + else + return string.Empty; + } + } + public override string H323Id + { + get + { + if (CodecConfiguration.Configuration.H323.H323Alias.ID != null) + return CodecConfiguration.Configuration.H323.H323Alias.ID.Value; + else + return string.Empty; + } + } + public override string SipPhoneNumber + { + get + { + if (CodecStatus.Status.SIP.Registration.Count > 0) + { + var match = Regex.Match(CodecStatus.Status.SIP.Registration[0].URI.Value, @"(\d+)"); // extract numbers only + if (match.Success) + { + Debug.Console(1, "Extracted phone number as '{0}' from string '{1}'", match.Groups[1].Value, CodecStatus.Status.SIP.Registration[0].URI.Value); + return match.Groups[1].Value; + } + else + { + Debug.Console(1, "Unable to extract phone number from string: '{0}'", CodecStatus.Status.SIP.Registration[0].URI.Value); + return string.Empty; + } + } + else + { + Debug.Console(1, "Unable to extract phone number. No SIP Registration items found"); + return string.Empty; + } + } + } + public override string SipUri + { + get + { + if (CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value != null) + return CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value; + else + return string.Empty; + } + } + public override bool AutoAnswerEnabled + { + get + { + if (CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value.ToLower() == "on") + return true; + else + return false; + } + } + + public CiscoCodecInfo(CiscoCodecStatus.RootObject status, CiscoCodecConfiguration.RootObject configuration) + { + CodecStatus = status; + CodecConfiguration = configuration; + } + } + + + #region IHasCameraPresets Members + + public event EventHandler CodecRoomPresetsListHasChanged; + + public List NearEndPresets { get; private set; } + + public List FarEndRoomPresets { get; private set; } + + public void CodecRoomPresetSelect(int preset) + { + Debug.Console(1, this, "Selecting Preset: {0}", preset); + if (SelectedCamera is IAmFarEndCamera) + SelectFarEndPreset(preset); + else + SendText(string.Format("xCommand RoomPreset Activate PresetId: {0}", preset)); + } + + public void CodecRoomPresetStore(int preset, string description) + { + SendText(string.Format("xCommand RoomPreset Store PresetId: {0} Description: \"{1}\" Type: All", preset, description)); + } + + #endregion + + public void SelectFarEndPreset(int preset) + { + SendText(string.Format("xCommand Call FarEndControl RoomPreset Activate CallId: {0} PresetId: {1}", GetCallId(), preset)); + } + + } + + /// + /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes + /// + public class CodecCommandWithLabel + { + public string Command { get; set; } + public string Label { get; set; } + + public CodecCommandWithLabel(string command, string label) + { + Command = command; + Label = label; + } + } + + /// + /// Tracks the initial sycnronization state of the codec when making a connection + /// + public class CodecSyncState : IKeyed + { + bool _InitialSyncComplete; + + public event EventHandler InitialSyncCompleted; + + public string Key { get; private set; } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + handler(this, new EventArgs()); + } + _InitialSyncComplete = value; + } + } + + public bool LoginMessageWasReceived { get; private set; } + + public bool InitialStatusMessageWasReceived { get; private set; } + + public bool InitialConfigurationMessageWasReceived { get; private set; } + + public bool FeedbackWasRegistered { get; private set; } + + public CodecSyncState(string key) + { + Key = key; + CodecDisconnected(); + } + + public void LoginMessageReceived() + { + LoginMessageWasReceived = true; + Debug.Console(1, this, "Login Message Received."); + CheckSyncStatus(); + } + + public void InitialStatusMessageReceived() + { + InitialStatusMessageWasReceived = true; + Debug.Console(1, this, "Initial Codec Status Message Received."); + CheckSyncStatus(); + } + + public void InitialConfigurationMessageReceived() + { + InitialConfigurationMessageWasReceived = true; + Debug.Console(1, this, "Initial Codec Configuration Message Received."); + CheckSyncStatus(); + } + + public void FeedbackRegistered() + { + FeedbackWasRegistered = true; + Debug.Console(1, this, "Initial Codec Feedback Registration Successful."); + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + LoginMessageWasReceived = false; + InitialConfigurationMessageWasReceived = false; + InitialStatusMessageWasReceived = false; + FeedbackWasRegistered = false; + InitialSyncComplete = false; + } + + void CheckSyncStatus() + { + if (LoginMessageWasReceived && InitialConfigurationMessageWasReceived && InitialStatusMessageWasReceived && FeedbackWasRegistered) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Codec Sync Complete!"); + } + else + InitialSyncComplete = false; + } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs.orig b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs.orig new file mode 100644 index 00000000..9ce58609 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs.orig @@ -0,0 +1,1487 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Net.Https; +using Crestron.SimplSharp.CrestronXml; +using Crestron.SimplSharp.CrestronXml.Serialization; +using Newtonsoft.Json; +//using Cisco_One_Button_To_Push; +//using Cisco_SX80_Corporate_Phone_Book; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.Occupancy; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + enum eCommandType { SessionStart, SessionEnd, Command, GetStatus, GetConfiguration }; + + public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, + IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfview, + ICommunicationMonitor, IRouting + { + public event EventHandler DirectoryResultReturned; + + public CommunicationGather PortGather { get; private set; } + public CommunicationGather JsonGather { get; private set; } + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + public BoolFeedback PresentationViewMaximizedFeedback { get; private set; } + + string CurrentPresentationView; + + public BoolFeedback RoomIsOccupiedFeedback { get; private set; } + + public IntFeedback PeopleCountFeedback { get; private set; } + + public BoolFeedback SpeakerTrackIsOnFeedback { get; private set; } + + public BoolFeedback SelfviewIsOnFeedback { get; private set; } + + public StringFeedback SelfviewPipPositionFeedback { get; private set; } + + public StringFeedback LocalLayoutFeedback { get; private set; } + + private CodecCommandWithLabel CurrentSelfviewPipPosition; + + private CodecCommandWithLabel CurrentLocalLayout; + + /// + /// List the available positions for the selfview PIP window + /// + public List SelfviewPipPositions = new List() + { + new CodecCommandWithLabel("CenterLeft", "Center Left"), + new CodecCommandWithLabel("CenterRight", "Center Right"), + new CodecCommandWithLabel("LowerLeft", "Lower Left"), + new CodecCommandWithLabel("LowerRight", "Lower Right"), + new CodecCommandWithLabel("UpperCenter", "Upper Center"), + new CodecCommandWithLabel("UpperLeft", "Upper Left"), + new CodecCommandWithLabel("UpperRight", "Upper Right"), + }; + + /// + /// Lists the available options for local layout + /// + public List LocalLayouts = new List() + { + //new CodecCommandWithLabel("auto", "Auto"), + //new CiscoCodecLocalLayout("custom", "Custom"), // Left out for now + new CodecCommandWithLabel("equal","Equal"), + new CodecCommandWithLabel("overlay","Overlay"), + new CodecCommandWithLabel("prominent","Prominent"), + new CodecCommandWithLabel("single","Single") + }; + + private CiscoCodecConfiguration.RootObject CodecConfiguration = new CiscoCodecConfiguration.RootObject(); + + private CiscoCodecStatus.RootObject CodecStatus = new CiscoCodecStatus.RootObject(); + + public CodecCallHistory CallHistory { get; private set; } + + public CodecCallFavorites CallFavorites { get; private set; } + + public CodecDirectory DirectoryRoot { get; private set; } + + public CodecScheduleAwareness CodecSchedule { get; private set; } + + /// + /// Gets and returns the scaled volume of the codec + /// + protected override Func VolumeLevelFeedbackFunc + { + get + { + return () => CrestronEnvironment.ScaleWithLimits(CodecStatus.Status.Audio.Volume.IntValue, 100, 0, 65535, 0); + } + } + + protected override Func PrivacyModeIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Audio.Microphones.Mute.BoolValue; + } + } + + protected override Func StandbyIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Standby.State.BoolValue; + } + } + + /// + /// Gets the value of the currently shared source, or returns null + /// + protected override Func SharingSourceFeedbackFunc + { + get + { + return () => PresentationSourceKey; + } + } + + protected override Func SharingContentIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Conference.Presentation.Mode.BoolValue; + } + } + + protected override Func MuteFeedbackFunc + { + get + { + return () => CodecStatus.Status.Audio.VolumeMute.BoolValue; + } + } + + protected Func RoomIsOccupiedFeedbackFunc + { + get + { + return () => CodecStatus.Status.RoomAnalytics.PeoplePresence.BoolValue; + } + } + + protected Func PeopleCountFeedbackFunc + { + get + { + return () => CodecStatus.Status.RoomAnalytics.PeopleCount.Current.IntValue; + } + } + + protected Func SpeakerTrackIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Cameras.SpeakerTrack.Status.BoolValue; + } + } + + protected Func SelfViewIsOnFeedbackFunc + { + get + { + return () => CodecStatus.Status.Video.Selfview.Mode.BoolValue; + } + } + + protected Func SelfviewPipPositionFeedbackFunc + { + get + { + return () => CurrentSelfviewPipPosition.Label; + } + } + + protected Func LocalLayoutFeedbackFunc + { + get + { + return () => CurrentLocalLayout.Label; + } + } + + + private string CliFeedbackRegistrationExpression; + + private CodecSyncState SyncState; + + public CodecPhonebookSyncState PhonebookSyncState { get; private set; } + + private StringBuilder JsonMessage; + + private bool JsonFeedbackMessageIsIncoming; + + public bool CommDebuggingIsOn; + + string Delimiter = "\r\n"; + + /// + /// Used to track the current connector used for the presentation source + /// + int PresentationSource; + + string PresentationSourceKey; + + string PhonebookMode = "Local"; // Default to Local + + int PhonebookResultsLimit = 255; // Could be set later by config. + + CTimer LoginMessageReceived; + + // **___________________________________________________________________** + // Timers to be moved to the global system timer at a later point.... + CTimer BookingsRefreshTimer; + CTimer PhonebookRefreshTimer; + // **___________________________________________________________________** + + public RoutingInputPort CodecOsdIn { get; private set; } + public RoutingInputPort HdmiIn2 { get; private set; } + public RoutingInputPort HdmiIn3 { get; private set; } + public RoutingOutputPort HdmiOut1 { get; private set; } + public RoutingOutputPort HdmiOut2 { get; private set; } + + + // Constructor for IBasicCommunication + public CiscoSparkCodec(string key, string name, IBasicCommunication comm, CiscoSparkCodecPropertiesConfig props ) + : base(key, name) + { + RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); + PeopleCountFeedback = new IntFeedback(PeopleCountFeedbackFunc); + SpeakerTrackIsOnFeedback = new BoolFeedback(SpeakerTrackIsOnFeedbackFunc); + SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); + SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); + LocalLayoutFeedback = new StringFeedback(LocalLayoutFeedbackFunc); +<<<<<<< HEAD +======= + LocalLayoutIsProminentFeedback = new BoolFeedback(LocalLayoutIsProminentFeedbackFunc); + FarEndIsSharingContentFeedback = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); +>>>>>>> development + + PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized"); + + Communication = comm; + + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "xStatus SystemUnit Software Version\r"); + } + + if (props.Sharing != null) + AutoShareContentWhileInCall = props.Sharing.AutoShareContentWhileInCall; + + ShowSelfViewByDefault = props.ShowSelfViewByDefault; + + DeviceManager.AddDevice(CommunicationMonitor); + + PhonebookMode = props.PhonebookMode; + + SyncState = new CodecSyncState(key + "--Sync"); + + PhonebookSyncState = new CodecPhonebookSyncState(key + "--PhonebookSync"); + + SyncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted); + + PortGather = new CommunicationGather(Communication, Delimiter); + PortGather.IncludeDelimiter = true; + PortGather.LineReceived += this.Port_LineReceived; + + CodecInfo = new CiscoCodecInfo(CodecStatus, CodecConfiguration); + + CallHistory = new CodecCallHistory(); + + if (props.Favorites != null) + { + CallFavorites = new CodecCallFavorites(); + CallFavorites.Favorites = props.Favorites; + } + + DirectoryRoot = new CodecDirectory(); + + CodecSchedule = new CodecScheduleAwareness(); + + //Set Feedback Actions + CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate; + CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate; + CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate; + CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate; + CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate; + CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate; + CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = SpeakerTrackIsOnFeedback.FireUpdate; + CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate; + CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus; + CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout; +<<<<<<< HEAD + CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = SharingContentIsOnFeedback.FireUpdate; +======= + CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction += SharingContentIsOnFeedback.FireUpdate; + CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction += FarEndIsSharingContentFeedback.FireUpdate; +>>>>>>> development + + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); + HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this); + HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource2), this); + + HdmiOut1 = new RoutingOutputPort(RoutingPortNames.HdmiOut1, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + HdmiOut2 = new RoutingOutputPort(RoutingPortNames.HdmiOut2, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + InputPorts.Add(CodecOsdIn); + InputPorts.Add(HdmiIn2); + InputPorts.Add(HdmiIn3); + OutputPorts.Add(HdmiOut1); + + CreateOsdSource(); + } + + + /// + /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input + /// to enable routing + /// + void CreateOsdSource() + { + OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); + DeviceManager.AddDevice(OsdSource); + var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); + TieLineCollection.Default.Add(tl); + } + + /// + /// Starts the HTTP feedback server and syncronizes state of codec + /// + /// + public override bool CustomActivate() + { + CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); + + Communication.Connect(); + LoginMessageReceived = new CTimer(DisconnectClientAndReconnect, 5000); + + var socket = Communication as ISocketStatus; + if (socket != null) + { + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + + CommunicationMonitor.Start(); + + string prefix = "xFeedback register "; + + CliFeedbackRegistrationExpression = + prefix + "/Configuration" + Delimiter + + prefix + "/Status/Audio" + Delimiter + + prefix + "/Status/Call" + Delimiter + + prefix + "/Status/Conference/Presentation" + Delimiter + + prefix + "/Status/Cameras/SpeakerTrack" + Delimiter + + prefix + "/Status/RoomAnalytics" + Delimiter + + prefix + "/Status/Standby" + Delimiter + + prefix + "/Status/Video/Selfview" + Delimiter + + prefix + "/Status/Video/Layout" + Delimiter + + prefix + "/Bookings" + Delimiter + + prefix + "/Event/CallDisconnect" + Delimiter + + prefix + "/Event/Bookings" + Delimiter; + + return base.CustomActivate(); + } + + /// + /// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc. + /// + /// + /// + void SyncState_InitialSyncCompleted(object sender, EventArgs e) + { + // Fire the ready event + SetIsReady(); + //CommDebuggingIsOn = false; + + GetCallHistory(); + + PhonebookRefreshTimer = new CTimer(CheckCurrentHour, 3600000, 3600000); // check each hour to see if the phonebook should be downloaded + GetPhonebook(null); + + BookingsRefreshTimer = new CTimer(GetBookings, 900000, 900000); // 15 minute timer to check for new booking info + GetBookings(null); + } + + public void SetCommDebug(string s) + { + if (s == "1") + { + CommDebuggingIsOn = true; + Debug.Console(0, this, "Comm Debug Enabled."); + } + else + { + CommDebuggingIsOn = false; + Debug.Console(0, this, "Comm Debug Disabled."); + } + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + if (e.Client.IsConnected) + { + LoginMessageReceived.Reset(5000); + } + else + { + SyncState.CodecDisconnected(); + PhonebookSyncState.CodecDisconnected(); + + if (PhonebookRefreshTimer != null) + { + PhonebookRefreshTimer.Stop(); + PhonebookRefreshTimer = null; + } + + if (BookingsRefreshTimer != null) + { + BookingsRefreshTimer.Stop(); + BookingsRefreshTimer = null; + } + } + } + + void DisconnectClientAndReconnect(object o) + { + Debug.Console(0, this, "Disconnecting and Reconnecting to codec."); + + Communication.Disconnect(); + + CrestronEnvironment.Sleep(2000); + + Communication.Connect(); + } + + /// + /// Gathers responses from the codec (including the delimiter. Responses are checked to see if they contain JSON data and if so, the data is collected until a complete JSON + /// message is received before forwarding the message to be deserialized. + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + if (CommDebuggingIsOn) + { + if(!JsonFeedbackMessageIsIncoming) + Debug.Console(1, this, "RX: '{0}'", args.Text); + } + + if (args.Text == "{" + Delimiter) // Check for the beginning of a new JSON message + { + JsonFeedbackMessageIsIncoming = true; + + if (CommDebuggingIsOn) + Debug.Console(1, this, "Incoming JSON message..."); + + JsonMessage = new StringBuilder(); + } + else if (args.Text == "}" + Delimiter) // Check for the end of a JSON message + { + JsonFeedbackMessageIsIncoming = false; + + JsonMessage.Append(args.Text); + + if (CommDebuggingIsOn) + Debug.Console(1, this, "Complete JSON Received:\n{0}", JsonMessage.ToString()); + + // Forward the complete message to be deserialized + DeserializeResponse(JsonMessage.ToString()); + return; + } + + if(JsonFeedbackMessageIsIncoming) + { + JsonMessage.Append(args.Text); + + //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); + return; + } + + if (!SyncState.InitialSyncComplete) + { + switch (args.Text.Trim().ToLower()) // remove the whitespace + { + case "*r login successful": + { + LoginMessageReceived.Stop(); + SendText("xPreferences outputmode json"); + break; + } + case "xpreferences outputmode json": + { + if (!SyncState.InitialStatusMessageWasReceived) + SendText("xStatus"); + break; + } + case "xfeedback register /event/calldisconnect": + { + SyncState.FeedbackRegistered(); + break; + } + } + } + + } + + public void SendText(string command) + { + if (CommDebuggingIsOn) + Debug.Console(1, this, "Sending: '{0}'", command); + + Communication.SendText(command + Delimiter); + } + + void DeserializeResponse(string response) + { + try + { + // Serializer settings. We want to ignore null values and mising members + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.NullValueHandling = NullValueHandling.Ignore; + settings.MissingMemberHandling = MissingMemberHandling.Ignore; + settings.ObjectCreationHandling = ObjectCreationHandling.Auto; + + if (response.IndexOf("\"Status\":{") > -1) + { + // Status Message + + // Temp object so we can inpsect for call data before simply deserializing + CiscoCodecStatus.RootObject tempCodecStatus = new CiscoCodecStatus.RootObject(); + + JsonConvert.PopulateObject(response, tempCodecStatus); + + // Check to see if the message contains /Status/Conference/Presentation/LocalInstance and extract source value + var conference = tempCodecStatus.Status.Conference; + + if (conference.Presentation.LocalInstance.Count > 0) + { + if (!string.IsNullOrEmpty(conference.Presentation.LocalInstance[0].ghost)) + PresentationSource = 0; + else if (conference.Presentation.LocalInstance[0].Source != null) + { + PresentationSource = conference.Presentation.LocalInstance[0].Source.IntValue; + } + } + + // Check to see if this is a call status message received after the initial status message + if (tempCodecStatus.Status.Call.Count > 0) + { + // Iterate through the call objects in the response + foreach (CiscoCodecStatus.Call call in tempCodecStatus.Status.Call) + { + var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(call.id)); + + if (tempActiveCall != null) + { + bool changeDetected = false; + + eCodecCallStatus newStatus = eCodecCallStatus.Unknown; + + // Update properties of ActiveCallItem + if(call.Status != null) + if (!string.IsNullOrEmpty(call.Status.Value)) + { + tempActiveCall.Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value); + + if (newStatus == eCodecCallStatus.Connected) + GetCallHistory(); + + changeDetected = true; + } + if (call.CallType != null) + if (!string.IsNullOrEmpty(call.CallType.Value)) + { + tempActiveCall.Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value); + changeDetected = true; + } + if (call.DisplayName != null) + if (!string.IsNullOrEmpty(call.DisplayName.Value)) + { + tempActiveCall.Name = call.DisplayName.Value; + changeDetected = true; + } + if (call.Direction != null) + { + if (!string.IsNullOrEmpty(call.Direction.Value)) + { + tempActiveCall.Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value); + changeDetected = true; + } + } + + if (changeDetected) + { + SetSelfViewMode(); + OnCallStatusChange(tempActiveCall); + ListCalls(); + } + } + else if( call.ghost == null ) // if the ghost value is present the call has ended already + { + // Create a new call item + var newCallItem = new CodecActiveCallItem() + { + Id = call.id, + Status = CodecCallStatus.ConvertToStatusEnum(call.Status.Value), + Name = call.DisplayName.Value, + Number = call.RemoteNumber.Value, + Type = CodecCallType.ConvertToTypeEnum(call.CallType.Value), + Direction = CodecCallDirection.ConvertToDirectionEnum(call.Direction.Value) + }; + + // Add it to the ActiveCalls List + ActiveCalls.Add(newCallItem); + + ListCalls(); + + SetSelfViewMode(); + OnCallStatusChange(newCallItem); + } + + } + + } + + JsonConvert.PopulateObject(response, CodecStatus); + + if (!SyncState.InitialStatusMessageWasReceived) + { + SyncState.InitialStatusMessageReceived(); + + if (!SyncState.InitialConfigurationMessageWasReceived) + SendText("xConfiguration"); + } + } + else if (response.IndexOf("\"Configuration\":{") > -1) + { + // Configuration Message + + JsonConvert.PopulateObject(response, CodecConfiguration); + + if (!SyncState.InitialConfigurationMessageWasReceived) + { + SyncState.InitialConfigurationMessageReceived(); + if (!SyncState.FeedbackWasRegistered) + { + SendText(CliFeedbackRegistrationExpression); + } + } + + } + else if (response.IndexOf("\"Event\":{") > -1) + { + if (response.IndexOf("\"CallDisconnect\":{") > -1) + { + CiscoCodecEvents.RootObject eventReceived = new CiscoCodecEvents.RootObject(); + + JsonConvert.PopulateObject(response, eventReceived); + + EvalutateDisconnectEvent(eventReceived); + } + else if (response.IndexOf("\"Bookings\":{") > -1) // The list has changed, reload it + { + GetBookings(null); + } + } + else if (response.IndexOf("\"CommandResponse\":{") > -1) + { + // CommandResponse Message + + if (response.IndexOf("\"CallHistoryRecentsResult\":{") > -1) + { + var codecCallHistory = new CiscoCallHistory.RootObject(); + + JsonConvert.PopulateObject(response, codecCallHistory); + + CallHistory.ConvertCiscoCallHistoryToGeneric(codecCallHistory.CommandResponse.CallHistoryRecentsResult.Entry); + } + else if (response.IndexOf("\"CallHistoryDeleteEntryResult\":{") > -1) + { + GetCallHistory(); + } + else if (response.IndexOf("\"PhonebookSearchResult\":{") > -1) + { + var codecPhonebookResponse = new CiscoCodecPhonebook.RootObject(); + + JsonConvert.PopulateObject(response, codecPhonebookResponse); + + if (!PhonebookSyncState.InitialPhonebookFoldersWasReceived) + { + // Check if the phonebook has any folders + PhonebookSyncState.InitialPhonebookFoldersReceived(); + + PhonebookSyncState.SetPhonebookHasFolders(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.Folder.Count > 0); + + if (PhonebookSyncState.PhonebookHasFolders) + { + DirectoryRoot.AddFoldersToDirectory(CiscoCodecPhonebook.GetRootFoldersFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); + } + + // Get the number of contacts in the phonebook + GetPhonebookContacts(); + } + else if (!PhonebookSyncState.NumberOfContactsWasReceived) + { + // Store the total number of contacts in the phonebook + PhonebookSyncState.SetNumberOfContacts(Int32.Parse(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value)); + + DirectoryRoot.AddContactsToDirectory(CiscoCodecPhonebook.GetRootContactsFromSearchResult(codecPhonebookResponse.CommandResponse.PhonebookSearchResult)); + + PhonebookSyncState.PhonebookRootEntriesReceived(); + + PrintPhonebook(DirectoryRoot); + } + else if (PhonebookSyncState.InitialSyncComplete) + { + var directoryResults = new CodecDirectory(); + + if(codecPhonebookResponse.CommandResponse.PhonebookSearchResult.ResultInfo.TotalRows.Value != "0") + directoryResults = CiscoCodecPhonebook.ConvertCiscoPhonebookToGeneric(codecPhonebookResponse.CommandResponse.PhonebookSearchResult); + + PrintPhonebook(directoryResults); + + // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology + var handler = DirectoryResultReturned; + if (handler != null) + handler(this, new DirectoryEventArgs() { Directory = directoryResults }); + + // Fire some sort of callback delegate to the UI that requested the directory search results + } + } + else if (response.IndexOf("\"BookingsListResult\":{") > -1) + { + var codecBookings = new CiscoCodecBookings.RootObject(); + + JsonConvert.PopulateObject(response, codecBookings); + + if(codecBookings.CommandResponse.BookingsListResult.ResultInfo.TotalRows.Value != "0") + CodecSchedule.Meetings = CiscoCodecBookings.GetGenericMeetingsFromBookingResult(codecBookings.CommandResponse.BookingsListResult.Booking); + + BookingsRefreshTimer.Reset(900000, 900000); + } + + } + + } + catch (Exception ex) + { + Debug.Console(1, this, "Error Deserializing feedback from codec: {0}", ex); + } + } + + /// + /// Evaluates an event received from the codec + /// + /// + void EvalutateDisconnectEvent(CiscoCodecEvents.RootObject eventReceived) + { + if (eventReceived.Event.CallDisconnect != null) + { + var tempActiveCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(eventReceived.Event.CallDisconnect.CallId.Value)); + + // Remove the call from the Active calls list + if (tempActiveCall != null) + { + ActiveCalls.Remove(tempActiveCall); + + ListCalls(); + + SetSelfViewMode(); + // Notify of the call disconnection + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, tempActiveCall); + + GetCallHistory(); + } + } + } + + /// + /// + /// + /// + public override void ExecuteSwitch(object selector) + { + (selector as Action)(); + PresentationSourceKey = selector.ToString(); + } + + /// + /// This is necessary for devices that are "routers" in the middle of the path, even though it only has one output and + /// may only have one input. + /// + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + ExecuteSwitch(inputSelector); + PresentationSourceKey = inputSelector.ToString(); + } + + + /// + /// Gets the first CallId or returns null + /// + /// + private string GetCallId() + { + string callId = null; + + if (ActiveCalls.Count > 1) + { + foreach (CodecActiveCallItem call in ActiveCalls) ; + } + else if (ActiveCalls.Count == 1) + callId = ActiveCalls[0].Id; + + return callId; + + } + + public void GetCallHistory() + { + SendText("xCommand CallHistory Recents Limit: 20 Order: OccurrenceTime"); + } + + /// + /// Required for IHasScheduleAwareness + /// + public void GetSchedule() + { + GetBookings(null); + } + + /// + /// Gets the bookings for today + /// + /// + public void GetBookings(object command) + { + Debug.Console(1, this, "Retrieving Booking Info from Codec. Current Time: {0}", DateTime.Now.ToLocalTime()); + + SendText("xCommand Bookings List Days: 1 DayOffset: 0"); + } + + /// + /// Checks to see if it is 2am (or within that hour) and triggers a download of the phonebook + /// + /// + public void CheckCurrentHour(object o) + { + if (DateTime.Now.Hour == 2) + { + Debug.Console(1, this, "Checking hour to see if phonebook should be downloaded. Current hour is {0}", DateTime.Now.Hour); + + GetPhonebook(null); + PhonebookRefreshTimer.Reset(3600000, 3600000); + } + } + + /// + /// Triggers a refresh of the codec phonebook + /// + /// Just to allow this method to be called from a console command + public void GetPhonebook(string command) + { + PhonebookSyncState.CodecDisconnected(); + + DirectoryRoot = new CodecDirectory(); + + GetPhonebookFolders(); + } + + private void GetPhonebookFolders() + { + // Get Phonebook Folders (determine local/corporate from config, and set results limit) + SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Folder", PhonebookMode)); + } + + private void GetPhonebookContacts() + { + // Get Phonebook Folders (determine local/corporate from config, and set results limit) + SendText(string.Format("xCommand Phonebook Search PhonebookType: {0} ContactType: Contact Limit: {1}", PhonebookMode, PhonebookResultsLimit)); + } + + /// + /// Searches the codec phonebook for all contacts matching the search string + /// + /// + public void SearchDirectory(string searchString) + { + SendText(string.Format("xCommand Phonebook Search SearchString: \"{0}\" PhonebookType: {1} ContactType: Contact Limit: {2}", searchString, PhonebookMode, PhonebookResultsLimit)); + } + + /// + /// // Get contents of a specific folder in the phonebook + /// + /// + public void GetDirectoryFolderContents(string folderId) + { + SendText(string.Format("xCommand Phonebook Search FolderId: {0} PhonebookType: {1} ContactType: Any Limit: {2}", folderId, PhonebookMode, PhonebookResultsLimit)); + } + + void PrintPhonebook(CodecDirectory directory) + { + if (Debug.Level > 0) + { + Debug.Console(1, this, "Directory Results:\n"); + + foreach (DirectoryItem item in directory.DirectoryResults) + { + if (item is DirectoryFolder) + { + Debug.Console(1, this, "[+] {0}", item.Name); + } + else if (item is DirectoryContact) + { + Debug.Console(1, this, "{0}", item.Name); + } + } + } + } + + /// + /// Simple dial method + /// + /// + public override void Dial(string number) + { + SendText(string.Format("xCommand Dial Number: \"{0}\"", number)); + } + + /// + /// Dials a specific meeting + /// + /// + public override void Dial(Meeting meeting) + { + foreach (Call c in meeting.Calls) + { + Dial(c.Number, c.Protocol, c.CallRate, c.CallType, meeting.Id); + } + } + + /// + /// Detailed dial method + /// + /// + /// + /// + /// + /// + public void Dial(string number, string protocol, string callRate, string callType, string meetingId) + { + SendText(string.Format("xCommand Dial Number: \"{0}\" Protocol: {1} CallRate: {2} CallType: {3} BookingId: {4}", number, protocol, callRate, callType, meetingId)); + } + + public override void EndCall(CodecActiveCallItem activeCall) + { + SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id)); + } + + public override void EndAllCalls() + { + foreach (CodecActiveCallItem activeCall in ActiveCalls) + { + SendText(string.Format("xCommand Call Disconnect CallId: {0}", activeCall.Id)); + } + } + + public override void AcceptCall(CodecActiveCallItem item) + { + SendText("xCommand Call Accept"); + } + + public override void RejectCall(CodecActiveCallItem item) + { + SendText("xCommand Call Reject"); + } + + public override void SendDtmf(string s) + { + if (CallFavorites != null) + { + SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s)); + } + } + + public void SelectPresentationSource(int source) + { + PresentationSource = source; + + StartSharing(); + } + + /// + /// Select source 1 as the presetnation source + /// + public void SelectPresentationSource1() + { + SelectPresentationSource(2); + } + + /// + /// Select source 2 as the presetnation source + /// + public void SelectPresentationSource2() + { + SelectPresentationSource(3); + } + + /// + /// Starts presentation sharing + /// + public override void StartSharing() + { + string sendingMode = string.Empty; + + if (IsInCall) + sendingMode = "LocalRemote"; + else + sendingMode = "LocalOnly"; + + if(PresentationSource > 0) + SendText(string.Format("xCommand Presentation Start PresentationSource: {0} SendingMode: {1}", PresentationSource, sendingMode)); + } + + /// + /// Stops sharing the current presentation + /// + public override void StopSharing() + { + PresentationSource = 0; + + SendText("xCommand Presentation Stop"); + } + + public override void PrivacyModeOn() + { + SendText("xCommand Audio Microphones Mute"); + } + + public override void PrivacyModeOff() + { + SendText("xCommand Audio Microphones Unmute"); + } + + public override void PrivacyModeToggle() + { + SendText("xCommand Audio Microphones ToggleMute"); + } + + public override void MuteOff() + { + SendText("xCommand Audio Volume Unmute"); + } + + public override void MuteOn() + { + SendText("xCommand Audio Volume Mute"); + } + + public override void MuteToggle() + { + SendText("xCommand Audio Volume ToggleMute"); + } + + /// + /// Increments the voluem + /// + /// + public override void VolumeUp(bool pressRelease) + { + SendText("xCommand Audio Volume Increase"); + } + + /// + /// Decrements the volume + /// + /// + public override void VolumeDown(bool pressRelease) + { + SendText("xCommand Audio Volume Decrease"); + } + + /// + /// Scales the level and sets the codec to the specified level within its range + /// + /// level from slider (0-65535 range) + public override void SetVolume(ushort level) + { + var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0); + SendText(string.Format("xCommand Audio Volume Set Level: {0}", scaledLevel)); + } + + /// + /// Recalls the default volume on the codec + /// + public void VolumeSetToDefault() + { + SendText("xCommand Audio Volume SetToDefault"); + } + + /// + /// Puts the codec in standby mode + /// + public override void StandbyActivate() + { + SendText("xCommand Standby Activate"); + } + + /// + /// Wakes the codec from standby + /// + public override void StandbyDeactivate() + { + SendText("xCommand Standby Deactivate"); + } + + /// + /// Reboots the codec + /// + public void Reboot() + { + SendText("xCommand SystemUnit Boot Action: Restart"); + } + + /// + /// Sets SelfView Mode based on config + /// + void SetSelfViewMode() + { + if (!IsInCall) + { + SelfviewModeOff(); + } + else + { + if (ShowSelfViewByDefault) + SelfviewModeOn(); + else + SelfviewModeOff(); + } + } + + /// + /// Turns on Selfview Mode + /// + public void SelfviewModeOn() + { + SendText("xCommand Video Selfview Set Mode: On"); + } + + /// + /// Turns off Selfview Mode + /// + public void SelfviewModeOff() + { + SendText("xCommand Video Selfview Set Mode: Off"); + } + + /// + /// Toggles Selfview mode on/off + /// + public void SelfviewModeToggle() + { + string mode = string.Empty; + + if (CodecStatus.Status.Video.Selfview.Mode.BoolValue) + mode = "Off"; + else + mode = "On"; + + SendText(string.Format("xCommand Video Selfview Set Mode: {0}", mode)); + } + + /// + /// Sets a specified position for the selfview PIP window + /// + /// + public void SelfviewPipPositionSet(CodecCommandWithLabel position) + { + SendText(string.Format("xCommand Video Selfview Set Mode: On PIPPosition: {0}", position.Command)); + } + + /// + /// Toggles to the next selfview PIP position + /// + public void SelfviewPipPositionToggle() + { + if (CurrentSelfviewPipPosition != null) + { + var nextPipPositionIndex = SelfviewPipPositions.IndexOf(CurrentSelfviewPipPosition) + 1; + + if (nextPipPositionIndex >= SelfviewPipPositions.Count) // Check if we need to loop back to the first item in the list + nextPipPositionIndex = 0; + + SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]); + } + } + + /// + /// Sets a specific local layout + /// + /// + public void LocalLayoutSet(CodecCommandWithLabel layout) + { + SendText(string.Format("xCommand Video Layout LayoutFamily Set Target: local LayoutFamily: {0}", layout.Command)); + } + + /// + /// Toggles to the next local layout + /// + public void LocalLayoutToggle() + { + if(CurrentLocalLayout != null) + { + var nextLocalLayoutIndex = LocalLayouts.IndexOf(CurrentLocalLayout) + 1; + + if (nextLocalLayoutIndex >= LocalLayouts.Count) // Check if we need to loop back to the first item in the list + nextLocalLayoutIndex = 0; + + LocalLayoutSet(LocalLayouts[nextLocalLayoutIndex]); + } + } + + /// + /// + /// + public void MinMaxLayoutToggle() + { + if (PresentationViewMaximizedFeedback.BoolValue) + CurrentPresentationView = "Minimized"; + else + CurrentPresentationView = "Maximized"; + + SendText(string.Format("xCommand Video PresentationView Set View: {0}", CurrentPresentationView)); + PresentationViewMaximizedFeedback.FireUpdate(); + } + + /// + /// Calculates the current selfview PIP position + /// + void ComputeSelfviewPipStatus() + { + CurrentSelfviewPipPosition = SelfviewPipPositions.FirstOrDefault(p => p.Command.ToLower().Equals(CodecStatus.Status.Video.Selfview.PIPPosition.Value.ToLower())); + + if(CurrentSelfviewPipPosition != null) + SelfviewIsOnFeedback.FireUpdate(); + } + + /// + /// Calculates the current local Layout + /// + void ComputeLocalLayout() + { + CurrentLocalLayout = LocalLayouts.FirstOrDefault(l => l.Command.ToLower().Equals(CodecStatus.Status.Video.Layout.LayoutFamily.Local.Value.ToLower())); + + if (CurrentLocalLayout != null) + LocalLayoutFeedback.FireUpdate(); + } + + public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry) + { + SendText(string.Format("xCommand CallHistory DeleteEntry CallHistoryId: {0} AcknowledgeConsecutiveDuplicates: True", entry.OccurrenceHistoryId)); + } + + public class CiscoCodecInfo : VideoCodecInfo + { + public CiscoCodecStatus.RootObject CodecStatus { get; private set; } + + public CiscoCodecConfiguration.RootObject CodecConfiguration { get; private set; } + + public override bool MultiSiteOptionIsEnabled + { + get + { + if (CodecStatus.Status.SystemUnit.Software.OptionKeys.MultiSite.Value.ToLower() == "true") + return true; + else + return false; + } + + } + public override string IpAddress + { + get + { + if (CodecConfiguration.Configuration.Network != null) + { + if (CodecConfiguration.Configuration.Network.Count > 0) + return CodecConfiguration.Configuration.Network[0].IPv4.Address.Value; + } + return string.Empty; + } + } + public override string E164Alias + { + get + { + if (CodecConfiguration.Configuration.H323.H323Alias.E164 != null) + return CodecConfiguration.Configuration.H323.H323Alias.E164.Value; + else + return string.Empty; + } + } + public override string H323Id + { + get + { + if (CodecConfiguration.Configuration.H323.H323Alias.ID != null) + return CodecConfiguration.Configuration.H323.H323Alias.ID.Value; + else + return string.Empty; + } + } + public override string SipPhoneNumber + { + get + { + if (CodecStatus.Status.SIP.Registration.Count > 0) + { + var match = Regex.Match(CodecStatus.Status.SIP.Registration[0].URI.Value, @"(\d+)"); // extract numbers only + if (match.Success) + { + Debug.Console(1, "Extracted phone number as '{0}' from string '{1}'", match.Groups[1].Value, CodecStatus.Status.SIP.Registration[0].URI.Value); + return match.Groups[1].Value; + } + else + { + Debug.Console(1, "Unable to extract phone number from string: '{0}'", CodecStatus.Status.SIP.Registration[0].URI.Value); + return string.Empty; + } + } + else + { + Debug.Console(1, "Unable to extract phone number. No SIP Registration items found"); + return string.Empty; + } + } + } + public override string SipUri + { + get + { + if (CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value != null) + return CodecStatus.Status.SIP.AlternateURI.Primary.URI.Value; + else + return string.Empty; + } + } + public override bool AutoAnswerEnabled + { + get + { + if (CodecConfiguration.Configuration.Conference.AutoAnswer.Mode.Value.ToLower() == "on") + return true; + else + return false; + } + } + + public CiscoCodecInfo(CiscoCodecStatus.RootObject status, CiscoCodecConfiguration.RootObject configuration) + { + CodecStatus = status; + CodecConfiguration = configuration; + } + } + } + + /// + /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes + /// + public class CodecCommandWithLabel + { + public string Command { get; set; } + public string Label { get; set; } + + public CodecCommandWithLabel(string command, string label) + { + Command = command; + Label = label; + } + } + + /// + /// Tracks the initial sycnronization state of the codec when making a connection + /// + public class CodecSyncState : IKeyed + { + bool _InitialSyncComplete; + + public event EventHandler InitialSyncCompleted; + + public string Key { get; private set; } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + handler(this, new EventArgs()); + } + _InitialSyncComplete = value; + } + } + + public bool InitialStatusMessageWasReceived { get; private set; } + + public bool InitialConfigurationMessageWasReceived { get; private set; } + + public bool FeedbackWasRegistered { get; private set; } + + public CodecSyncState(string key) + { + Key = key; + CodecDisconnected(); + } + + public void InitialStatusMessageReceived() + { + InitialStatusMessageWasReceived = true; + Debug.Console(1, this, "Initial Codec Status Message Received."); + CheckSyncStatus(); + } + + public void InitialConfigurationMessageReceived() + { + InitialConfigurationMessageWasReceived = true; + Debug.Console(1, this, "Initial Codec Configuration Message Received."); + CheckSyncStatus(); + } + + public void FeedbackRegistered() + { + FeedbackWasRegistered = true; + Debug.Console(1, this, "Initial Codec Feedback Registration Successful."); + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + InitialConfigurationMessageWasReceived = false; + InitialStatusMessageWasReceived = false; + FeedbackWasRegistered = false; + InitialSyncComplete = false; + } + + void CheckSyncStatus() + { + if (InitialConfigurationMessageWasReceived && InitialStatusMessageWasReceived && FeedbackWasRegistered) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Codec Sync Complete!"); + } + else + InitialSyncComplete = false; + } + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs new file mode 100644 index 00000000..33cda37c --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodecPropertiesConfig.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + public class CiscoSparkCodecPropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + + public List Favorites { get; set; } + + /// + /// Valid values: "Local" or "Corporate" + /// + public string PhonebookMode { get; set; } + + public bool ShowSelfViewByDefault { get; set; } + + public SharingProperties Sharing { get; set; } + + } + + public class SharingProperties + { + public bool AutoShareContentWhileInCall { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/HttpApiServer.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/HttpApiServer.cs new file mode 100644 index 00000000..affe9c2f --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/HttpApiServer.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Net.Http; + +using PepperDash.Core; + + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + public class HttpApiServer + { + public static Dictionary ExtensionContentTypes; + + public event EventHandler ApiRequest; + public Crestron.SimplSharp.Net.Http.HttpServer HttpServer { get; private set; } + + public string HtmlRoot { get; set; } + + + /// + /// SIMPL+ can only execute the default constructor. If you have variables that require initialization, please + /// use an Initialize method + /// + public HttpApiServer() + { + ExtensionContentTypes = new Dictionary + { + { ".css", "text/css" }, + { ".htm", "text/html" }, + { ".html", "text/html" }, + { ".jpg", "image/jpeg" }, + { ".jpeg", "image/jpeg" }, + { ".js", "application/javascript" }, + { ".json", "application/json" }, + { ".xml", "text/xml" }, + { ".map", "application/x-navimap" }, + { ".pdf", "application.pdf" }, + { ".png", "image/png" }, + { ".txt", "text/plain" }, + }; + HtmlRoot = @"\HTML"; + } + + + public void Start(int port) + { + // TEMP - this should be inserted by configuring class + + HttpServer = new Crestron.SimplSharp.Net.Http.HttpServer(); + HttpServer.ServerName = "Cisco API Server"; + HttpServer.KeepAlive = true; + HttpServer.Port = port; + HttpServer.OnHttpRequest += Server_Request; + HttpServer.Open(); + + CrestronEnvironment.ProgramStatusEventHandler += (a) => + { + if (a == eProgramStatusEventType.Stopping) + { + HttpServer.Close(); + Debug.Console(1, "Shutting down HTTP Server on port {0}", HttpServer.Port); + } + }; + } + + void Server_Request(object sender, OnHttpRequestArgs args) + { + if (args.Request.Header.RequestType == "OPTIONS") + { + Debug.Console(2, "Asking for OPTIONS"); + args.Response.Header.SetHeaderValue("Access-Control-Allow-Origin", "*"); + args.Response.Header.SetHeaderValue("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS"); + return; + } + + string path = Uri.UnescapeDataString(args.Request.Path); + var host = args.Request.DataConnection.RemoteEndPointAddress; + //string authToken; + + Debug.Console(2, "HTTP Request: {2}: Path='{0}' ?'{1}'", path, args.Request.QueryString, host); + + // ----------------------------------- ADD AUTH HERE + if (path.StartsWith("/cisco/api")) + { + var handler = ApiRequest; + if (ApiRequest != null) + ApiRequest(this, args); + } + } + + public static string GetContentType(string extension) + { + string type; + if (ExtensionContentTypes.ContainsKey(extension)) + type = ExtensionContentTypes[extension]; + else + type = "text/plain"; + return type; + } + + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/PhonebookDataClasses.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/PhonebookDataClasses.cs new file mode 100644 index 00000000..c88261f5 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/PhonebookDataClasses.cs @@ -0,0 +1,397 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Devices.Common.Codec; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public class CiscoCodecPhonebook + { + public class Offset + { + public string Value { get; set; } + } + + public class Limit + { + public string Value { get; set; } + } + + public class TotalRows + { + public string Value { get; set; } + } + + public class ResultInfo + { + public Offset Offset { get; set; } + public Limit Limit { get; set; } + public TotalRows TotalRows { get; set; } + } + + public class LocalId + { + public string Value { get; set; } + } + + public class FolderId + { + public string Value { get; set; } + } + + public class ParentFolderId + { + public string Value { get; set; } + } + + public class Name + { + public string Value { get; set; } + } + + public class Folder + { + public string id { get; set; } + public LocalId LocalId { get; set; } + public FolderId FolderId { get; set; } + public Name Name { get; set; } + public ParentFolderId ParentFolderId { get; set; } + } + + public class Name2 + { + public string Value { get; set; } + } + + public class ContactId + { + public string Value { get; set; } + } + + public class FolderId2 + { + public string Value { get; set; } + } + + public class Title + { + public string Value { get; set; } + } + + public class ContactMethodId + { + public string Value { get; set; } + } + + public class Number + { + public string Value { get; set; } + } + + public class Device + { + public string Value { get; set; } + } + + public class CallType + { + public string Value { get; set; } + } + + public class ContactMethod + { + public string id { get; set; } + public ContactMethodId ContactMethodId { get; set; } + public Number Number { get; set; } + public Device Device { get; set; } + public CallType CallType { get; set; } + + public ContactMethod() + { + ContactMethodId = new ContactMethodId(); + Number = new Number(); + Device = new Device(); + CallType = new CallType(); + } + } + + public class Contact + { + public string id { get; set; } + public Name2 Name { get; set; } + public ContactId ContactId { get; set; } + public FolderId2 FolderId { get; set; } + public Title Title { get; set; } + public List ContactMethod { get; set; } + + public Contact() + { + Name = new Name2(); + ContactId = new ContactId(); + FolderId = new FolderId2(); + Title = new Title(); + ContactMethod = new List(); + } + } + + public class PhonebookSearchResult + { + public string status { get; set; } + public ResultInfo ResultInfo { get; set; } + public List Folder { get; set; } + public List Contact { get; set; } + + public PhonebookSearchResult() + { + Folder = new List(); + Contact = new List(); + ResultInfo = new ResultInfo(); + } + } + + public class CommandResponse + { + public PhonebookSearchResult PhonebookSearchResult { get; set; } + } + + public class RootObject + { + public CommandResponse CommandResponse { get; set; } + + } + + + /// + /// Extracts the folders with no ParentFolder and returns them sorted alphabetically + /// + /// + /// + public static List GetRootFoldersFromSearchResult(PhonebookSearchResult result) + { + var rootFolders = new List(); + + if (result.Folder.Count == 0) + { + return null; + } + else if (result.Folder.Count > 0) + { + if (Debug.Level > 0) + Debug.Console(1, "Phonebook Folders:\n"); + + foreach (Folder f in result.Folder) + { + var folder = new DirectoryFolder(); + + folder.Name = f.Name.Value; + folder.FolderId = f.FolderId.Value; + + if (f.ParentFolderId == null) + rootFolders.Add(folder); + + if (Debug.Level > 0) + Debug.Console(1, "+ {0}", folder.Name); + } + } + + rootFolders.OrderBy(f => f.Name); + + return rootFolders; + } + + + /// + /// Extracts the contacts with no FolderId and returns them sorted alphabetically + /// + /// + /// + public static List GetRootContactsFromSearchResult(PhonebookSearchResult result) + { + var rootContacts = new List(); + + if (result.Contact.Count == 0) + { + return null; + } + else if (result.Contact.Count > 0) + { + if (Debug.Level > 0) + Debug.Console(1, "Root Contacts:\n"); + + foreach (Contact c in result.Contact) + { + var contact = new DirectoryContact(); + + if (string.IsNullOrEmpty(c.FolderId.Value)) + { + contact.Name = c.Name.Value; + contact.ContactId = c.ContactId.Value; + + if(!string.IsNullOrEmpty(c.Title.Value)) + contact.Title = c.Title.Value; + + if (Debug.Level > 0) + Debug.Console(1, "{0}\nContact Methods:", contact.Name); + + foreach (ContactMethod m in c.ContactMethod) + { + + var tempContactMethod = new PepperDash.Essentials.Devices.Common.Codec.ContactMethod(); + + eContactMethodCallType callType = eContactMethodCallType.Unknown; + if (!string.IsNullOrEmpty(m.CallType.Value)) + { + if (!string.IsNullOrEmpty(m.CallType.Value)) + { + if (m.CallType.Value.ToLower() == "audio") + callType = eContactMethodCallType.Audio; + else if (m.CallType.Value.ToLower() == "video") + callType = eContactMethodCallType.Video; + + tempContactMethod.CallType = callType; + } + } + + eContactMethodDevice device = eContactMethodDevice.Unknown; + if (!string.IsNullOrEmpty(m.Device.Value)) + { + + if (m.Device.Value.ToLower() == "mobile") + device = eContactMethodDevice.Mobile; + else if (m.Device.Value.ToLower() == "telephone") + device = eContactMethodDevice.Telephone; + else if (m.Device.Value.ToLower() == "video") + device = eContactMethodDevice.Video; + else if (m.Device.Value.ToLower() == "other") + device = eContactMethodDevice.Other; + + tempContactMethod.Device = device; + } + + if (Debug.Level > 0) + Debug.Console(1, "Number: {0}", m.Number.Value); + + tempContactMethod.Number = m.Number.Value; + tempContactMethod.ContactMethodId = m.ContactMethodId.Value; + + contact.ContactMethods.Add(tempContactMethod); + } + rootContacts.Add(contact); + } + } + } + + rootContacts.OrderBy(f => f.Name); + + return rootContacts; + } + + + /// + /// Converts data returned from a cisco codec to the generic Directory format. + /// + /// + /// + /// + public static CodecDirectory ConvertCiscoPhonebookToGeneric(PhonebookSearchResult result) + { + var directory = new Codec.CodecDirectory(); + + var folders = new List(); + + var contacts = new List(); + + try + { + if (result.Folder.Count > 0) + { + foreach (Folder f in result.Folder) + { + var folder = new DirectoryFolder(); + + folder.Name = f.Name.Value; + folder.FolderId = f.FolderId.Value; + + if (f.ParentFolderId != null) + { + folder.ParentFolderId = f.ParentFolderId.Value; + } + + folders.Add(folder); + } + + folders.OrderBy(f => f.Name); + + directory.AddFoldersToDirectory(folders); + } + + if (result.Contact.Count > 0) + { + foreach (Contact c in result.Contact) + { + var contact = new DirectoryContact(); + + contact.Name = c.Name.Value; + contact.ContactId = c.ContactId.Value; + if (!string.IsNullOrEmpty(c.Title.Value)) + contact.Title = c.Title.Value; + + if (c.FolderId != null) + { + contact.FolderId = c.FolderId.Value; + } + + foreach (ContactMethod m in c.ContactMethod) + { + eContactMethodCallType callType = eContactMethodCallType.Unknown; + if (!string.IsNullOrEmpty(m.CallType.Value)) + { + if (m.CallType.Value.ToLower() == "audio") + callType = eContactMethodCallType.Audio; + else if (m.CallType.Value.ToLower() == "video") + callType = eContactMethodCallType.Video; + } + + eContactMethodDevice device = eContactMethodDevice.Unknown; + + if (!string.IsNullOrEmpty(m.Device.Value)) + { + if (m.Device.Value.ToLower() == "mobile") + device = eContactMethodDevice.Mobile; + else if (m.Device.Value.ToLower() == "telephone") + device = eContactMethodDevice.Telephone; + else if (m.Device.Value.ToLower() == "video") + device = eContactMethodDevice.Video; + else if (m.Device.Value.ToLower() == "other") + device = eContactMethodDevice.Other; + } + + contact.ContactMethods.Add(new PepperDash.Essentials.Devices.Common.Codec.ContactMethod() + { + Number = m.Number.Value, + ContactMethodId = m.ContactMethodId.Value, + CallType = callType, + Device = device + }); + } + contacts.Add(contact); + } + + contacts.OrderBy(c => c.Name); + + directory.AddContactsToDirectory(contacts); + } + } + catch (Exception e) + { + Debug.Console(1, "Error converting Cisco Phonebook results to generic: {0}", e); + } + + return directory; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs new file mode 100644 index 00000000..2846674a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/RoomPresets.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; + +using PepperDash.Core; +using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + /// + /// Interface for camera presets + /// + public interface IHasCodecRoomPresets + { + event EventHandler CodecRoomPresetsListHasChanged; + + List NearEndPresets { get; } + + List FarEndRoomPresets { get; } + + void CodecRoomPresetSelect(int preset); + + void CodecRoomPresetStore(int preset, string description); + } + + public static class RoomPresets + { + /// + /// Converts Cisco RoomPresets to generic CameraPresets + /// + /// + /// + public static List GetGenericPresets(List presets) + { + var cameraPresets = new List(); + + if (Debug.Level > 0) + { + Debug.Console(1, "Presets List:"); + } + + foreach (CiscoCodecStatus.RoomPreset preset in presets) + { + try + { + var cameraPreset = new CodecRoomPreset(UInt16.Parse(preset.id), preset.Description.Value, preset.Defined.BoolValue, true); + + cameraPresets.Add(cameraPreset); + + if (Debug.Level > 0) + { + Debug.Console(1, "Added Preset ID: {0}, Description: {1}, IsDefined: {2}, isDefinable: {3}", cameraPreset.ID, cameraPreset.Description, cameraPreset.Defined, cameraPreset.IsDefinable); + } + } + catch (Exception e) + { + Debug.Console(2, "Unable to convert preset: {0}. Error: {1}", preset.id, e); + } + } + + return cameraPresets; + } + } + + /// + /// Represents a room preset on a video coded. Typically stores camera position(s) and video routing. Can be recalled by Far End if enabled. + /// + public class CodecRoomPreset + { + [JsonProperty("id")] + public int ID { get; set; } + /// + /// Used to store the name of the preset + /// + [JsonProperty("description")] + public string Description { get; set; } + /// + /// Indicates if the preset is defined(stored) in the codec + /// + [JsonProperty("defined")] + public bool Defined { get; set; } + /// + /// Indicates if the preset has the capability to be defined + /// + [JsonProperty("isDefinable")] + public bool IsDefinable { get; set; } + + public CodecRoomPreset(int id, string description, bool def, bool isDef) + { + ID = id; + Description = description; + Defined = def; + IsDefinable = isDef; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs new file mode 100644 index 00000000..8ae9e643 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xConfiguration.cs @@ -0,0 +1,1807 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Crestron.SimplSharp; + +using PepperDash.Core; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + /// + /// This class exists to capture serialized data sent back by a Cisco codec in JSON output mode + /// + public class CiscoCodecConfiguration + { + public class DefaultVolume + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Dereverberation + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class NoiseReduction + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class EchoControl + { + public Dereverberation Dereverberation { get; set; } + public Mode Mode { get; set; } + public NoiseReduction NoiseReduction { get; set; } + } + + public class Level + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Microphone + { + public string id { get; set; } + public EchoControl EchoControl { get; set; } + public Level Level { get; set; } + public Mode2 Mode { get; set; } + } + + public class Input + { + public List Microphone { get; set; } + } + + public class Enabled + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mute + { + public Enabled Enabled { get; set; } + } + + public class Microphones + { + public Mute Mute { get; set; } + } + + public class Mode3 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class InternalSpeaker + { + public Mode3 Mode { get; set; } + } + + public class Output + { + public InternalSpeaker InternalSpeaker { get; set; } + } + + public class RingTone + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class RingVolume + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SoundsAndAlerts + { + public RingTone RingTone { get; set; } + public RingVolume RingVolume { get; set; } + } + + public class Audio + { + public DefaultVolume DefaultVolume { get; set; } + public Input Input { get; set; } + public Microphones Microphones { get; set; } + public Output Output { get; set; } + public SoundsAndAlerts SoundsAndAlerts { get; set; } + } + + public class DefaultMode + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Backlight + { + public DefaultMode DefaultMode { get; set; } + } + + public class DefaultLevel + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode4 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Brightness + { + public DefaultLevel DefaultLevel { get; set; } + public Mode4 Mode { get; set; } + } + + public class Mode5 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Focus + { + public Mode5 Mode { get; set; } + } + + public class Level2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode6 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Gamma + { + public Level2 Level { get; set; } + public Mode6 Mode { get; set; } + } + + public class Mirror + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Level3 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode7 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Whitebalance + { + public Level3 Level { get; set; } + public Mode7 Mode { get; set; } + } + + public class Framerate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Camera + { + public string id { get; set; } + public Framerate Framerate { get; set; } + public Backlight Backlight { get; set; } + public Brightness Brightness { get; set; } + public Focus Focus { get; set; } + public Gamma Gamma { get; set; } + public Mirror Mirror { get; set; } + public Whitebalance Whitebalance { get; set; } + } + + public class Closeup + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode8 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SpeakerTrack + { + public Closeup Closeup { get; set; } + public Mode8 Mode { get; set; } + } + + public class Cameras + { + //[JsonConverter(typeof(CameraConverter)), JsonProperty("Camera")] + //public List Camera { get; set; } + //[JsonProperty("SpeakerTrack")] + public SpeakerTrack SpeakerTrack { get; set; } + + public Cameras() + { + //Camera = new List(); + SpeakerTrack = new SpeakerTrack(); + } + } + + public class CameraConverter : JsonConverter + { + // this is currently not working + public override bool CanConvert(System.Type objectType) + { + return objectType == typeof(Camera) || objectType == typeof(List); // This should not be called but is required for implmentation + } + + public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer) + { + try + { + if (reader.TokenType == JsonToken.StartArray) + { + var l = new List(); + reader.Read(); + while (reader.TokenType != JsonToken.EndArray) + { + l.Add(reader.Value as Camera); + reader.Read(); + } + Debug.Console(1, "[xConfiguration]: Cameras converted as list"); + return l; + } + else + { + Debug.Console(1, "[xConfiguration]: Camera converted as single object and added to list"); + return new List { reader.Value as Camera }; + } + } + catch (Exception e) + { + Debug.Console(1, "[xConfiguration]: Unable to convert JSON for camera objects: {0}", e); + + return new List(); + } + } + + public override bool CanWrite + { + get + { + return false; + } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException("Write not implemented"); + } + } + + public class Delay + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode9 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mute2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class AutoAnswer + { + public Delay Delay { get; set; } + public Mode9 Mode { get; set; } + public Mute2 Mute { get; set; } + } + + public class Protocol + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Rate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class DefaultCall + { + public Protocol Protocol { get; set; } + public Rate Rate { get; set; } + } + + public class DefaultTimeout + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class DoNotDisturb + { + public DefaultTimeout DefaultTimeout { get; set; } + } + + public class Mode10 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Encryption + { + public Mode10 Mode { get; set; } + } + + public class Mode11 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class FarEndControl + { + public Mode11 Mode { get; set; } + } + + public class MaxReceiveCallRate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class MaxTotalReceiveCallRate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class MaxTotalTransmitCallRate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class MaxTransmitCallRate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode12 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class MultiStream + { + public Mode12 Mode { get; set; } + } + + public class Conference + { + public AutoAnswer AutoAnswer { get; set; } + public DefaultCall DefaultCall { get; set; } + public DoNotDisturb DoNotDisturb { get; set; } + public Encryption Encryption { get; set; } + public FarEndControl FarEndControl { get; set; } + public MaxReceiveCallRate MaxReceiveCallRate { get; set; } + public MaxTotalReceiveCallRate MaxTotalReceiveCallRate { get; set; } + public MaxTotalTransmitCallRate MaxTotalTransmitCallRate { get; set; } + public MaxTransmitCallRate MaxTransmitCallRate { get; set; } + public MultiStream MultiStream { get; set; } + } + + public class LoginName + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode13 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Password + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Authentication + { + public LoginName LoginName { get; set; } + public Mode13 Mode { get; set; } + public Password Password { get; set; } + } + + public class Mode14 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class CallSetup + { + public Mode14 Mode { get; set; } + } + + public class KeySize + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Encryption2 + { + public KeySize KeySize { get; set; } + } + + public class Address + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Gatekeeper + { + public Address Address { get; set; } + } + + public class E164 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class ID + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class H323Alias + { + public E164 E164 { get; set; } + public ID ID { get; set; } + } + + public class Address2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode15 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class NAT + { + public Address2 Address { get; set; } + public Mode15 Mode { get; set; } + } + + public class H323 + { + public Authentication Authentication { get; set; } + public CallSetup CallSetup { get; set; } + public Encryption2 Encryption { get; set; } + public Gatekeeper Gatekeeper { get; set; } + public H323Alias H323Alias { get; set; } + public NAT NAT { get; set; } + } + + public class Name + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Domain + { + public Name Name { get; set; } + } + + public class Address3 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Server + { + public string id { get; set; } + public Address3 Address { get; set; } + } + + public class DNS + { + public Domain Domain { get; set; } + public List Server { get; set; } + } + + public class AnonymousIdentity + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Md5 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Peap + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Tls + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Ttls + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Eap + { + public Md5 Md5 { get; set; } + public Peap Peap { get; set; } + public Tls Tls { get; set; } + public Ttls Ttls { get; set; } + } + + public class Identity + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode16 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Password2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class TlsVerify + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class UseClientCertificate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class IEEE8021X + { + public AnonymousIdentity AnonymousIdentity { get; set; } + public Eap Eap { get; set; } + public Identity Identity { get; set; } + public Mode16 Mode { get; set; } + public Password2 Password { get; set; } + public TlsVerify TlsVerify { get; set; } + public UseClientCertificate UseClientCertificate { get; set; } + } + + public class IPStack + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Address4 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Assignment + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Gateway + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SubnetMask + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class IPv4 + { + public Address4 Address { get; set; } + public Assignment Assignment { get; set; } + public Gateway Gateway { get; set; } + public SubnetMask SubnetMask { get; set; } + } + + public class Address5 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Assignment2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class DHCPOptions + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Gateway2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class IPv6 + { + public Address5 Address { get; set; } + public Assignment2 Assignment { get; set; } + public DHCPOptions DHCPOptions { get; set; } + public Gateway2 Gateway { get; set; } + } + + public class MTU + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Audio2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Data + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class ICMPv6 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class NTP + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Signalling + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Video + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Diffserv + { + public Audio2 Audio { get; set; } + public Data Data { get; set; } + public ICMPv6 ICMPv6 { get; set; } + public NTP NTP { get; set; } + public Signalling Signalling { get; set; } + public Video Video { get; set; } + } + + public class Mode17 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class QoS + { + public Diffserv Diffserv { get; set; } + public Mode17 Mode { get; set; } + } + + public class Allow + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class RemoteAccess + { + public Allow Allow { get; set; } + } + + public class Speed + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode18 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class VlanId + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Voice + { + public Mode18 Mode { get; set; } + public VlanId VlanId { get; set; } + } + + public class VLAN + { + public Voice Voice { get; set; } + } + + public class Network + { + public string id { get; set; } + public DNS DNS { get; set; } + public IEEE8021X IEEE8021X { get; set; } + public IPStack IPStack { get; set; } + public IPv4 IPv4 { get; set; } + public IPv6 IPv6 { get; set; } + public MTU MTU { get; set; } + public QoS QoS { get; set; } + public RemoteAccess RemoteAccess { get; set; } + public Speed Speed { get; set; } + public VLAN VLAN { get; set; } + } + + public class Mode19 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class CDP + { + public Mode19 Mode { get; set; } + } + + public class Mode20 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class H3232 + { + public Mode20 Mode { get; set; } + } + + public class Mode21 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class HTTP + { + public Mode21 Mode { get; set; } + } + + public class MinimumTLSVersion + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Server2 + { + public MinimumTLSVersion MinimumTLSVersion { get; set; } + } + + public class StrictTransportSecurity + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class VerifyClientCertificate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class VerifyServerCertificate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class HTTPS + { + public Server2 Server { get; set; } + public StrictTransportSecurity StrictTransportSecurity { get; set; } + public VerifyClientCertificate VerifyClientCertificate { get; set; } + public VerifyServerCertificate VerifyServerCertificate { get; set; } + } + + public class Mode22 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Address6 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Server3 + { + public string id { get; set; } + public Address6 Address { get; set; } + } + + public class NTP2 + { + public Mode22 Mode { get; set; } + public List Server { get; set; } + } + + public class Mode23 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SIP + { + public Mode23 Mode { get; set; } + } + + public class CommunityName + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Address7 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Host + { + public string id { get; set; } + public Address7 Address { get; set; } + } + + public class Mode24 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SystemContact + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SystemLocation + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SNMP + { + public CommunityName CommunityName { get; set; } + public List Host { get; set; } + public Mode24 Mode { get; set; } + public SystemContact SystemContact { get; set; } + public SystemLocation SystemLocation { get; set; } + } + + public class Mode25 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SSH + { + public Mode25 Mode { get; set; } + } + + public class Mode26 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class UPnP + { + public Mode26 Mode { get; set; } + } + + public class WelcomeText + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class NetworkServices + { + public CDP CDP { get; set; } + public H3232 H323 { get; set; } + public HTTP HTTP { get; set; } + public HTTPS HTTPS { get; set; } + public NTP2 NTP { get; set; } + public SIP SIP { get; set; } + public SNMP SNMP { get; set; } + public SSH SSH { get; set; } + public UPnP UPnP { get; set; } + public WelcomeText WelcomeText { get; set; } + } + + public class Cameras2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class ControlSystems + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class TouchPanels + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Profile + { + public Cameras2 Cameras { get; set; } + public ControlSystems ControlSystems { get; set; } + public TouchPanels TouchPanels { get; set; } + } + + public class Peripherals + { + public Profile Profile { get; set; } + } + + public class ID2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Type + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class URL + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Server4 + { + public string id { get; set; } + public ID2 ID { get; set; } + public Type Type { get; set; } + public URL URL { get; set; } + } + + public class Phonebook + { + public List Server { get; set; } + } + + public class Connectivity + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Address8 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class AlternateAddress + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Domain2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Path + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Protocol2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class ExternalManager + { + public Address8 Address { get; set; } + public AlternateAddress AlternateAddress { get; set; } + public Domain2 Domain { get; set; } + public Path Path { get; set; } + public Protocol2 Protocol { get; set; } + } + + public class HttpMethod + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class LoginName2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode27 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Password3 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Provisioning + { + public Connectivity Connectivity { get; set; } + public ExternalManager ExternalManager { get; set; } + public HttpMethod HttpMethod { get; set; } + public LoginName2 LoginName { get; set; } + public Mode27 Mode { get; set; } + public Password3 Password { get; set; } + } + + public class Mode28 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class CallControl + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class FromClients + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class ToClients + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class ContentShare + { + public FromClients FromClients { get; set; } + public ToClients ToClients { get; set; } + } + + public class Services + { + public CallControl CallControl { get; set; } + public ContentShare ContentShare { get; set; } + } + + public class Proximity + { + public Mode28 Mode { get; set; } + public Services Services { get; set; } + } + + public class PeopleCountOutOfCall + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class PeoplePresenceDetector + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class RoomAnalytics + { + public PeopleCountOutOfCall PeopleCountOutOfCall { get; set; } + public PeoplePresenceDetector PeoplePresenceDetector { get; set; } + } + + public class Password4 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class UserName + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Authentication2 + { + public Password4 Password { get; set; } + public UserName UserName { get; set; } + } + + public class DefaultTransport + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class DisplayName + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class DefaultCandidate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode29 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Ice + { + public DefaultCandidate DefaultCandidate { get; set; } + public Mode29 Mode { get; set; } + } + + public class ListenPort + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Address9 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Proxy + { + public string id { get; set; } + public Address9 Address { get; set; } + } + + public class Password5 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Server5 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class UserName2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Turn + { + public Password5 Password { get; set; } + public Server5 Server { get; set; } + public UserName2 UserName { get; set; } + } + + public class URI + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SIP2 + { + public Authentication2 Authentication { get; set; } + public DefaultTransport DefaultTransport { get; set; } + public DisplayName DisplayName { get; set; } + public Ice Ice { get; set; } + public ListenPort ListenPort { get; set; } + public List Proxy { get; set; } + public Turn Turn { get; set; } + public URI URI { get; set; } + } + + public class BaudRate + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class LoginRequired + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode30 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SerialPort + { + public BaudRate BaudRate { get; set; } + public LoginRequired LoginRequired { get; set; } + public Mode30 Mode { get; set; } + } + + public class BootAction + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Control + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Delay2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class StandbyAction + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class WakeupAction + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Standby + { + public BootAction BootAction { get; set; } + public Control Control { get; set; } + public Delay2 Delay { get; set; } + public StandbyAction StandbyAction { get; set; } + public WakeupAction WakeupAction { get; set; } + } + + public class Name2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class SystemUnit + { + public Name2 Name { get; set; } + } + + public class DateFormat + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class TimeFormat + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Zone + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Time + { + public DateFormat DateFormat { get; set; } + public TimeFormat TimeFormat { get; set; } + public Zone Zone { get; set; } + } + + public class Type2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class ContactInfo + { + public Type2 Type { get; set; } + } + + public class Mode31 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class KeyTones + { + public Mode31 Mode { get; set; } + } + + public class Language + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Output2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class OSD + { + public Output2 Output { get; set; } + } + + public class UserInterface + { + public ContactInfo ContactInfo { get; set; } + public KeyTones KeyTones { get; set; } + public Language Language { get; set; } + public OSD OSD { get; set; } + } + + public class Filter + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Group + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Admin + { + public Filter Filter { get; set; } + public Group Group { get; set; } + } + + public class Attribute + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class BaseDN + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Encryption3 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class MinimumTLSVersion2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode32 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Address10 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Port + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Server6 + { + public Address10 Address { get; set; } + public Port Port { get; set; } + } + + public class VerifyServerCertificate2 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class LDAP + { + public Admin Admin { get; set; } + public Attribute Attribute { get; set; } + public BaseDN BaseDN { get; set; } + public Encryption3 Encryption { get; set; } + public MinimumTLSVersion2 MinimumTLSVersion { get; set; } + public Mode32 Mode { get; set; } + public Server6 Server { get; set; } + public VerifyServerCertificate2 VerifyServerCertificate { get; set; } + } + + public class UserManagement + { + public LDAP LDAP { get; set; } + } + + public class DefaultMainSource + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class CameraId + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode33 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class CameraControl + { + public CameraId CameraId { get; set; } + public Mode33 Mode { get; set; } + } + + public class InputSourceType + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Name3 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class PreferredResolution + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class PresentationSelection + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Quality + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Visibility + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Connector + { + public string id { get; set; } + public CameraControl CameraControl { get; set; } + public InputSourceType InputSourceType { get; set; } + public Name3 Name { get; set; } + public PreferredResolution PreferredResolution { get; set; } + public PresentationSelection PresentationSelection { get; set; } + public Quality Quality { get; set; } + public Visibility Visibility { get; set; } + } + + public class Input2 + { + public List Connector { get; set; } + } + + public class Monitors + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode34 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class CEC + { + public Mode34 Mode { get; set; } + } + + public class MonitorRole + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Resolution + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Connector2 + { + public string id { get; set; } + public CEC CEC { get; set; } + public MonitorRole MonitorRole { get; set; } + public Resolution Resolution { get; set; } + } + + public class Output3 + { + public List Connector { get; set; } + } + + public class DefaultSource + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Presentation + { + public DefaultSource DefaultSource { get; set; } + } + + public class FullscreenMode + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode35 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class OnMonitorRole + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class PIPPosition + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Default + { + public FullscreenMode FullscreenMode { get; set; } + public Mode35 Mode { get; set; } + public OnMonitorRole OnMonitorRole { get; set; } + public PIPPosition PIPPosition { get; set; } + } + + public class Duration + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class Mode36 + { + public string valueSpaceRef { get; set; } + public string Value { get; set; } + } + + public class OnCall + { + public Duration Duration { get; set; } + public Mode36 Mode { get; set; } + } + + public class Selfview + { + public Default Default { get; set; } + public OnCall OnCall { get; set; } + } + + public class Video2 + { + public DefaultMainSource DefaultMainSource { get; set; } + public Input2 Input { get; set; } + public Monitors Monitors { get; set; } + public Output3 Output { get; set; } + public Presentation Presentation { get; set; } + public Selfview Selfview { get; set; } + } + + public class Configuration + { + public Audio Audio { get; set; } + public Cameras Cameras { get; set; } + public Conference Conference { get; set; } + public H323 H323 { get; set; } + public List Network { get; set; } + public NetworkServices NetworkServices { get; set; } + public Peripherals Peripherals { get; set; } + public Phonebook Phonebook { get; set; } + public Provisioning Provisioning { get; set; } + public Proximity Proximity { get; set; } + public RoomAnalytics RoomAnalytics { get; set; } + public SIP2 SIP { get; set; } + public SerialPort SerialPort { get; set; } + public Standby Standby { get; set; } + public SystemUnit SystemUnit { get; set; } + public Time Time { get; set; } + public UserInterface UserInterface { get; set; } + public UserManagement UserManagement { get; set; } + public Video2 Video { get; set; } + } + + public class RootObject + { + public Configuration Configuration { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs new file mode 100644 index 00000000..342a8848 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xEvent.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + /// + /// This class exists to capture serialized data sent back by a Cisco codec in JSON output mode + /// + public class CiscoCodecEvents + { + public class CauseValue + { + public string id { get; set; } + public string Value { get; set; } + } + + public class CauseType + { + public string id { get; set; } + public string Value { get; set; } + } + + public class CauseString + { + public string id { get; set; } + public string Value { get; set; } + } + + public class OrigCallDirection + { + public string id { get; set; } + public string Value { get; set; } + } + + public class RemoteURI + { + public string id { get; set; } + public string Value { get; set; } + } + + public class DisplayName + { + public string id { get; set; } + public string Value { get; set; } + } + + public class CallId + { + public string id { get; set; } + public string Value { get; set; } + } + + public class CauseCode + { + public string id { get; set; } + public string Value { get; set; } + } + + public class CauseOrigin + { + public string id { get; set; } + public string Value { get; set; } + } + + public class Protocol + { + public string id { get; set; } + public string Value { get; set; } + } + + public class Duration + { + public string id { get; set; } + public string Value { get; set; } + } + + public class CallType + { + public string id { get; set; } + public string Value { get; set; } + } + + public class CallRate + { + public string id { get; set; } + public string Value { get; set; } + } + + public class Encryption + { + public string id { get; set; } + public string Value { get; set; } + } + + public class RequestedURI + { + public string id { get; set; } + public string Value { get; set; } + } + + public class PeopleCountAverage + { + public string id { get; set; } + public string Value { get; set; } + } + + public class CallDisconnect + { + public string id { get; set; } + public CauseValue CauseValue { get; set; } + public CauseType CauseType { get; set; } + public CauseString CauseString { get; set; } + public OrigCallDirection OrigCallDirection { get; set; } + public RemoteURI RemoteURI { get; set; } + public DisplayName DisplayName { get; set; } + public CallId CallId { get; set; } + public CauseCode CauseCode { get; set; } + public CauseOrigin CauseOrigin { get; set; } + public Protocol Protocol { get; set; } + public Duration Duration { get; set; } + public CallType CallType { get; set; } + public CallRate CallRate { get; set; } + public Encryption Encryption { get; set; } + public RequestedURI RequestedURI { get; set; } + public PeopleCountAverage PeopleCountAverage { get; set; } + } + + public class Event + { + public CallDisconnect CallDisconnect { get; set; } + } + + public class RootObject + { + public Event Event { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs new file mode 100644 index 00000000..9af52bd2 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs @@ -0,0 +1,2066 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronXml.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + /// + /// This class exists to capture serialized data sent back by a Cisco codec in JSON output mode + /// + public class CiscoCodecStatus + { + // Helper Classes for Proerties + public abstract class ValueProperty + { + /// + /// Triggered when Value is set + /// + public Action ValueChangedAction { get; set; } + + protected void OnValueChanged() + { + var a = ValueChangedAction; + if (a != null) + a(); + } + + } + + + public class ConnectionStatus + { + public string Value { get; set; } + } + + public class EcReferenceDelay + { + public string Value { get; set; } + } + + public class Microphone + { + public string id { get; set; } + public ConnectionStatus ConnectionStatus { get; set; } + public EcReferenceDelay EcReferenceDelay { get; set; } + } + + public class Connectors + { + public List Microphone { get; set; } + } + + public class Input + { + public Connectors Connectors { get; set; } + } + + public class Mute : ValueProperty + { + public bool BoolValue { get; private set; } + + public string Value + { + set + { + // If the incoming value is "On" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "On"; + OnValueChanged(); + } + } + } + + public class Microphones + { + public Mute Mute { get; set; } + + public Microphones() + { + Mute = new Mute(); + } + } + + public class ConnectionStatus2 + { + public string Value { get; set; } + } + + public class DelayMs + { + public string Value { get; set; } + } + + public class Line + { + public string id { get; set; } + public ConnectionStatus2 ConnectionStatus { get; set; } + public DelayMs DelayMs { get; set; } + } + + public class Connectors2 + { + public List Line { get; set; } + } + + public class Output + { + public Connectors2 Connectors { get; set; } + } + + public class Volume : ValueProperty + { + string _Value; + + /// + /// Sets Value and triggers the action when set + /// + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + OnValueChanged(); + } + } + + /// + /// Converted value of _Value for use as feedback + /// + public int IntValue + { + get + { + if (!string.IsNullOrEmpty(_Value)) + return Convert.ToInt32(_Value); + else + return 0; + } + } + } + + public class VolumeMute : ValueProperty + { + public bool BoolValue { get; private set; } + + public string Value + { + set + { + // If the incoming value is "On" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "On"; + OnValueChanged(); + } + } + } + + public class Audio + { + public Input Input { get; set; } + public Microphones Microphones { get; set; } // Can we have this setter fire the update on the CiscoCodec feedback? + public Output Output { get; set; } + public Volume Volume { get; set; } + public VolumeMute VolumeMute { get; set; } + + public Audio() + { + Volume = new Volume(); + VolumeMute = new VolumeMute(); + Microphones = new Microphones(); + } + } + + public class Id + { + public string Value { get; set; } + } + + public class Current + { + public Id Id { get; set; } + } + + public class Bookings + { + public Current Current { get; set; } + } + + public class Options + { + public string Value { get; set; } + } + + public class Capabilities + { + public Options Options { get; set; } + } + + public class Connected + { + public string Value { get; set; } + } + + public class Framerate + { + public string Value { get; set; } + } + + public class Flip + { + public string Value { get; set; } + } + + public class HardwareID + { + public string Value { get; set; } + } + + public class Manufacturer + { + public string Value { get; set; } + } + + public class Model + { + public string Value { get; set; } + } + + public class Pan + { + public string Value { get; set; } + } + + public class Tilt + { + public string Value { get; set; } + } + + public class Zoom + { + public string Value { get; set; } + } + + public class Position + { + public Pan Pan { get; set; } + public Tilt Tilt { get; set; } + public Zoom Zoom { get; set; } + } + + public class SoftwareID + { + public string Value { get; set; } + } + + public class Camera + { + public string id { get; set; } + public Capabilities Capabilities { get; set; } + public Connected Connected { get; set; } + public Flip Flip { get; set; } + public HardwareID HardwareID { get; set; } + public MacAddress MacAddress { get; set; } + public Manufacturer Manufacturer { get; set; } + public Model Model { get; set; } + public Position Position { get; set; } + public SerialNumber SerialNumber { get; set; } + public SoftwareID SoftwareID { get; set; } + } + + public class Availability + { + public string Value { get; set; } + } + + public class Status2 : ValueProperty + { + string _Value; + public bool BoolValue { get; private set; } + + public string Value + { + get + { + return _Value; + } + set + { + // If the incoming value is "Active" it sets the BoolValue true, otherwise sets it false + _Value = value; + BoolValue = value == "Active"; + OnValueChanged(); + } + } + } + + public class SpeakerTrack + { + public Availability Availability { get; set; } + public Status2 Status { get; set; } + + public SpeakerTrack() + { + Status = new Status2(); + } + } + + public class Cameras + { + // [JsonConverter(typeof(CameraConverter))] + public List Camera { get; set; } + public SpeakerTrack SpeakerTrack { get; set; } + + public Cameras() + { + Camera = new List(); + SpeakerTrack = new SpeakerTrack(); + } + } + + //public class CameraConverter : JsonConverter + //{ + + // public override bool CanConvert(System.Type objectType) + // { + // return true; // objectType == typeof(Camera) || objectType == typeof(List); // This should not be called but is required for implmentation + // } + + // public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer) + // { + // try + // { + // if (reader.TokenType == JsonToken.StartArray) + // { + // var l = new List(); + // reader.Read(); + // while (reader.TokenType != JsonToken.EndArray) + // { + // l.Add(reader.Value as Camera); + // reader.Read(); + // } + // Debug.Console(1, "[xStatus]: Cameras converted as list"); + // return l; + // } + // else + // { + // Debug.Console(1, "[xStatus]: Camera converted as single object and added to list"); + // return new List { reader.Value as Camera }; + // } + // } + // catch (Exception e) + // { + // Debug.Console(1, "[xStatus]: Unable to convert JSON for camera objects: {0}", e); + + // return new List(); + // } + // } + + // public override bool CanWrite + // { + // get + // { + // return false; + // } + // } + + // public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + // { + // throw new NotImplementedException("Write not implemented"); + // } + //} + + + public class MaxActiveCalls + { + public string Value { get; set; } + } + + public class MaxAudioCalls + { + public string Value { get; set; } + } + + public class MaxCalls + { + public string Value { get; set; } + } + + public class MaxVideoCalls + { + public string Value { get; set; } + } + + public class Conference + { + public MaxActiveCalls MaxActiveCalls { get; set; } + public MaxAudioCalls MaxAudioCalls { get; set; } + public MaxCalls MaxCalls { get; set; } + public MaxVideoCalls MaxVideoCalls { get; set; } + } + + public class Capabilities2 + { + public Conference Conference { get; set; } + } + + public class CallId + { + public string Value { get; set; } + } + + public class ActiveSpeaker + { + public CallId CallId { get; set; } + } + + public class DoNotDisturb + { + public string Value { get; set; } + } + + public class Mode + { + public string Value { get; set; } + } + + public class Multipoint + { + public Mode Mode { get; set; } + } + + public class CallId2 + { + public string Value { get; set; } + } + + public class Mode2 : ValueProperty + { + string _Value; + + public bool BoolValue { get; private set; } + + + + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + // If the incoming value is "Sending" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "Sending"; + OnValueChanged(); + } + } + } + + public class Mode3 + { + public string Value { get; set; } + } + + public class ReleaseFloorAvailability + { + public string Value { get; set; } + } + + public class RequestFloorAvailability + { + public string Value { get; set; } + } + + public class Whiteboard + { + public Mode3 Mode { get; set; } + public ReleaseFloorAvailability ReleaseFloorAvailability { get; set; } + public RequestFloorAvailability RequestFloorAvailability { get; set; } + } + + public class Source2 : ValueProperty + { + string _Value; + + /// + /// Sets Value and triggers the action when set + /// + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + OnValueChanged(); + } + } + + /// + /// Converted value of _Value for use as feedback + /// + public int IntValue + { + get + { + if (!string.IsNullOrEmpty(_Value)) + return Convert.ToInt32(_Value); + else + return 0; + } + } + } + + public class SendingMode + { + public string Value { get; set; } + } + + public class LocalInstance + { + public string id { get; set; } + public string ghost { get; set; } + public SendingMode SendingMode { get; set; } + public Source2 Source { get; set; } + + public LocalInstance() + { + Source = new Source2(); + } + } + + public class Presentation + { + public CallId2 CallId { get; set; } + public Mode2 Mode { get; set; } + public Whiteboard Whiteboard { get; set; } + public List LocalInstance { get; set; } + + public Presentation() + { + Mode = new Mode2(); + LocalInstance = new List(); + } + } + + public class CallId3 + { + public string Value { get; set; } + } + + public class Mode4 + { + public string Value { get; set; } + } + + public class SpeakerLock + { + public CallId3 CallId { get; set; } + public Mode4 Mode { get; set; } + } + + public class Conference2 + { + public ActiveSpeaker ActiveSpeaker { get; set; } + public DoNotDisturb DoNotDisturb { get; set; } + public Multipoint Multipoint { get; set; } + public Presentation Presentation { get; set; } + public SpeakerLock SpeakerLock { get; set; } + + public Conference2() + { + Presentation = new Presentation(); + } + } + + public class Description + { + public string Value { get; set; } + } + + public class Level + { + public string Value { get; set; } + } + + public class References + { + public string Value { get; set; } + } + + public class Type + { + public string Value { get; set; } + } + + public class Message + { + public string id { get; set; } + public Description Description { get; set; } + public Level Level { get; set; } + public References References { get; set; } + public Type Type { get; set; } + } + + public class Diagnostics + { + public List Message { get; set; } + } + + public class Conference3 + { + } + + public class Experimental + { + public Conference3 Conference { get; set; } + } + + public class Address + { + public string Value { get; set; } + } + + public class Port + { + public string Value { get; set; } + } + + public class Reason + { + public string Value { get; set; } + } + + public class Status3 + { + public string Value { get; set; } + } + + public class Gatekeeper + { + public Address Address { get; set; } + public Port Port { get; set; } + public Reason Reason { get; set; } + public Status3 Status { get; set; } + } + + public class Reason2 + { + public string Value { get; set; } + } + + public class Status4 + { + public string Value { get; set; } + } + + public class Mode5 + { + public Reason2 Reason { get; set; } + public Status4 Status { get; set; } + } + + public class H323 + { + public Gatekeeper Gatekeeper { get; set; } + public Mode5 Mode { get; set; } + } + + public class Expression + { + public string id { get; set; } + public string Value { get; set; } + } + + public class Format + { + public string Value { get; set; } + } + + public class URL + { + public string Value { get; set; } + } + + public class HttpFeedback + { + public string id { get; set; } + public List Expression { get; set; } + public Format Format { get; set; } + public URL URL { get; set; } + } + + public class MediaChannels + { + } + + public class Address2 + { + public string Value { get; set; } + } + + public class Capabilities3 + { + public string Value { get; set; } + } + + public class DeviceId + { + public string Value { get; set; } + } + + public class Duplex + { + public string Value { get; set; } + } + + public class Platform + { + public string Value { get; set; } + } + + public class PortID + { + public string Value { get; set; } + } + + public class PrimaryMgmtAddress + { + public string Value { get; set; } + } + + public class SysName + { + public string Value { get; set; } + } + + public class SysObjectID + { + public string Value { get; set; } + } + + public class VTPMgmtDomain + { + public string Value { get; set; } + } + + public class Version + { + public string Value { get; set; } + } + + public class VoIPApplianceVlanID + { + public string Value { get; set; } + } + + public class CDP + { + public Address2 Address { get; set; } + public Capabilities3 Capabilities { get; set; } + public DeviceId DeviceId { get; set; } + public Duplex Duplex { get; set; } + public Platform Platform { get; set; } + public PortID PortID { get; set; } + public PrimaryMgmtAddress PrimaryMgmtAddress { get; set; } + public SysName SysName { get; set; } + public SysObjectID SysObjectID { get; set; } + public VTPMgmtDomain VTPMgmtDomain { get; set; } + public Version Version { get; set; } + public VoIPApplianceVlanID VoIPApplianceVlanID { get; set; } + } + + public class Name + { + public string Value { get; set; } + } + + public class Domain + { + public Name Name { get; set; } + } + + public class Address3 + { + public string Value { get; set; } + } + + public class Server + { + public string id { get; set; } + public Address3 Address { get; set; } + } + + public class DNS + { + public Domain Domain { get; set; } + public List Server { get; set; } + } + + public class MacAddress + { + public string Value { get; set; } + } + + public class Speed + { + public string Value { get; set; } + } + + public class Ethernet + { + public MacAddress MacAddress { get; set; } + public Speed Speed { get; set; } + } + + public class Address4 + { + public string Value { get; set; } + } + + public class Gateway + { + public string Value { get; set; } + } + + public class SubnetMask + { + public string Value { get; set; } + } + + public class IPv4 + { + public Address4 Address { get; set; } + public Gateway Gateway { get; set; } + public SubnetMask SubnetMask { get; set; } + } + + public class Address5 + { + public string Value { get; set; } + } + + public class Gateway2 + { + public string Value { get; set; } + } + + public class IPv6 + { + public Address5 Address { get; set; } + public Gateway2 Gateway { get; set; } + } + + public class VlanId + { + public string Value { get; set; } + } + + public class Voice + { + public VlanId VlanId { get; set; } + } + + public class VLAN + { + public Voice Voice { get; set; } + } + + public class Network + { + public string id { get; set; } + public CDP CDP { get; set; } + public DNS DNS { get; set; } + public Ethernet Ethernet { get; set; } + public IPv4 IPv4 { get; set; } + public IPv6 IPv6 { get; set; } + public VLAN VLAN { get; set; } + } + + public class CurrentAddress + { + public string Value { get; set; } + } + + public class Address6 + { + public string Value { get; set; } + } + + public class Server2 + { + public string id { get; set; } + public Address6 Address { get; set; } + } + + public class Status5 + { + public string Value { get; set; } + } + + public class NTP + { + public CurrentAddress CurrentAddress { get; set; } + public List Server { get; set; } + public Status5 Status { get; set; } + } + + public class NetworkServices + { + public NTP NTP { get; set; } + } + + public class HardwareInfo + { + public string Value { get; set; } + } + + public class ID2 + { + public string Value { get; set; } + } + + public class Name2 + { + public string Value { get; set; } + } + + public class SoftwareInfo + { + public string Value { get; set; } + } + + public class Status6 + { + public string Value { get; set; } + } + + public class Type2 + { + public string Value { get; set; } + } + + public class UpgradeStatus + { + public string Value { get; set; } + } + + public class ConnectedDevice + { + public string id { get; set; } + public HardwareInfo HardwareInfo { get; set; } + public ID2 ID { get; set; } + public Name2 Name { get; set; } + public SoftwareInfo SoftwareInfo { get; set; } + public Status6 Status { get; set; } + public Type2 Type { get; set; } + public UpgradeStatus UpgradeStatus { get; set; } + } + + public class Peripherals + { + public List ConnectedDevice { get; set; } + } + + public class Enabled + { + public string Value { get; set; } + } + + public class LastLoggedInUserId + { + public string Value { get; set; } + } + + public class LoggedIn + { + public string Value { get; set; } + } + + public class ExtensionMobility + { + public Enabled Enabled { get; set; } + public LastLoggedInUserId LastLoggedInUserId { get; set; } + public LoggedIn LoggedIn { get; set; } + } + + public class CUCM + { + public ExtensionMobility ExtensionMobility { get; set; } + } + + public class CompletedAt + { + public string Value { get; set; } + } + + public class URL2 + { + public string Value { get; set; } + } + + public class VersionId + { + public string Value { get; set; } + } + + public class Current2 + { + public CompletedAt CompletedAt { get; set; } + public URL2 URL { get; set; } + public VersionId VersionId { get; set; } + } + + public class LastChange + { + public string Value { get; set; } + } + + public class Message2 + { + public string Value { get; set; } + } + + public class Phase + { + public string Value { get; set; } + } + + public class SessionId + { + public string Value { get; set; } + } + + public class Status7 + { + public string Value { get; set; } + } + + public class URL3 + { + public string Value { get; set; } + } + + public class VersionId2 + { + public string Value { get; set; } + } + + public class UpgradeStatus2 + { + public LastChange LastChange { get; set; } + public Message2 Message { get; set; } + public Phase Phase { get; set; } + public SessionId SessionId { get; set; } + public Status7 Status { get; set; } + public URL3 URL { get; set; } + public VersionId2 VersionId { get; set; } + } + + public class Software + { + public Current2 Current { get; set; } + public UpgradeStatus2 UpgradeStatus { get; set; } + } + + public class Status8 + { + public string Value { get; set; } + } + + public class Provisioning + { + public CUCM CUCM { get; set; } + public Software Software { get; set; } + public Status8 Status { get; set; } + } + + public class Availability2 + { + public string Value { get; set; } + } + + public class Services + { + public Availability2 Availability { get; set; } + } + + public class Proximity + { + public Services Services { get; set; } + } + + public class Current3 : ValueProperty + { + string _Value; + + /// + /// Sets Value and triggers the action when set + /// + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + OnValueChanged(); + } + } + + /// + /// Converted value of _Value for use as feedback + /// + public int IntValue + { + get + { + if (!string.IsNullOrEmpty(_Value)) + return Convert.ToInt32(_Value); + else + return 0; + } + } + } + + public class PeopleCount + { + public Current3 Current { get; set; } + + public PeopleCount() + { + Current = new Current3(); + } + } + + public class PeoplePresence : ValueProperty + { + public bool BoolValue { get; private set; } + + public string Value + { + set + { + // If the incoming value is "Yes" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "Yes"; + OnValueChanged(); + } + } + } + + public class RoomAnalytics + { + public PeopleCount PeopleCount { get; set; } + public PeoplePresence PeoplePresence { get; set; } + + public RoomAnalytics() + { + PeopleCount = new PeopleCount(); + PeoplePresence = new PeoplePresence(); + } + } + + public class Primary + { + public URI URI { get; set; } + + public Primary() + { + URI = new URI(); + } + } + + public class AlternateURI + { + public Primary Primary { get; set; } + + public AlternateURI() + { + Primary = new Primary(); + } + } + + public class Authentication + { + public string Value { get; set; } + } + + public class DisplayName + { + public string Value { get; set; } + } + + public class Mode6 + { + public string Value { get; set; } + } + + public class URI + { + public string Value { get; set; } + } + + public class CallForward + { + public DisplayName DisplayName { get; set; } + public Mode6 Mode { get; set; } + public URI URI { get; set; } + } + + public class MessagesWaiting + { + public string Value { get; set; } + } + + public class URI2 + { + public string Value { get; set; } + } + + public class Mailbox + { + public MessagesWaiting MessagesWaiting { get; set; } + public URI2 URI { get; set; } + } + + public class Address7 + { + public string Value { get; set; } + } + + public class Status9 + { + public string Value { get; set; } + } + + public class Proxy + { + public string id { get; set; } + public Address7 Address { get; set; } + public Status9 Status { get; set; } + } + + public class Reason3 + { + public string Value { get; set; } + } + + public class Status10 + { + public string Value { get; set; } + } + + public class URI3 + { + public string Value { get; set; } + } + + public class Registration + { + public string id { get; set; } + public Reason3 Reason { get; set; } + public Status10 Status { get; set; } + public URI3 URI { get; set; } + + public Registration() + { + URI = new URI3(); + } + } + + public class Secure + { + public string Value { get; set; } + } + + public class Verified + { + public string Value { get; set; } + } + + public class SIP + { + public AlternateURI AlternateURI { get; set; } + public Authentication Authentication { get; set; } + public CallForward CallForward { get; set; } + public Mailbox Mailbox { get; set; } + public List Proxy { get; set; } + public List Registration { get; set; } + public Secure Secure { get; set; } + public Verified Verified { get; set; } + + public SIP() + { + AlternateURI = new AlternateURI(); + Registration = new List(); + } + } + + public class Mode7 + { + public string Value { get; set; } + } + + public class FIPS + { + public Mode7 Mode { get; set; } + } + + public class CallHistory + { + public string Value { get; set; } + } + + public class Configurations + { + public string Value { get; set; } + } + + public class DHCP + { + public string Value { get; set; } + } + + public class InternalLogging + { + public string Value { get; set; } + } + + public class LocalPhonebook + { + public string Value { get; set; } + } + + public class Persistency + { + public CallHistory CallHistory { get; set; } + public Configurations Configurations { get; set; } + public DHCP DHCP { get; set; } + public InternalLogging InternalLogging { get; set; } + public LocalPhonebook LocalPhonebook { get; set; } + } + + public class Security + { + public FIPS FIPS { get; set; } + public Persistency Persistency { get; set; } + } + + public class State : ValueProperty + { + public bool BoolValue { get; private set; } + + public string Value // Valid values are Standby/EnteringStandby/Halfwake/Off + { + set + { + // If the incoming value is "Of" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "Off"; + OnValueChanged(); + } + } + } + + public class Standby + { + public State State { get; set; } + + public Standby() + { + State = new State(); + } + } + + public class CompatibilityLevel + { + public string Value { get; set; } + } + + public class SerialNumber + { + public string Value { get; set; } + } + + public class Module + { + public CompatibilityLevel CompatibilityLevel { get; set; } + public SerialNumber SerialNumber { get; set; } + } + + public class Hardware + { + public Module Module { get; set; } + } + + public class ProductId + { + public string Value { get; set; } + } + + public class ProductPlatform + { + public string Value { get; set; } + } + + public class ProductType + { + public string Value { get; set; } + } + + public class DisplayName2 + { + public string Value { get; set; } + } + + public class Name3 + { + public string Value { get; set; } + } + + public class Encryption + { + public string Value { get; set; } + } + + public class MultiSite + { + public string Value { get; set; } + } + + public class RemoteMonitoring + { + public string Value { get; set; } + } + + public class OptionKeys + { + public Encryption Encryption { get; set; } + public MultiSite MultiSite { get; set; } + public RemoteMonitoring RemoteMonitoring { get; set; } + + public OptionKeys() + { + MultiSite = new MultiSite(); + } + } + + public class ReleaseDate + { + public string Value { get; set; } + } + + public class Version2 + { + public string Value { get; set; } + } + + public class Software2 + { + public DisplayName2 DisplayName { get; set; } + public Name3 Name { get; set; } + public OptionKeys OptionKeys { get; set; } + public ReleaseDate ReleaseDate { get; set; } + public Version2 Version { get; set; } + + public Software2() + { + OptionKeys = new OptionKeys(); + } + } + + public class NumberOfActiveCalls + { + public string Value { get; set; } + } + + public class NumberOfInProgressCalls + { + public string Value { get; set; } + } + + public class NumberOfSuspendedCalls + { + public string Value { get; set; } + } + + public class State2 + { + public NumberOfActiveCalls NumberOfActiveCalls { get; set; } + public NumberOfInProgressCalls NumberOfInProgressCalls { get; set; } + public NumberOfSuspendedCalls NumberOfSuspendedCalls { get; set; } + } + + public class Uptime + { + public string Value { get; set; } + } + + public class SystemUnit + { + public Hardware Hardware { get; set; } + public ProductId ProductId { get; set; } + public ProductPlatform ProductPlatform { get; set; } + public ProductType ProductType { get; set; } + public Software2 Software { get; set; } + public State2 State { get; set; } + public Uptime Uptime { get; set; } + + public SystemUnit() + { + Software = new Software2(); + } + } + + public class SystemTime + { + public DateTime Value { get; set; } + } + + public class Time + { + public SystemTime SystemTime { get; set; } + } + + public class Number + { + public string Value { get; set; } + } + + public class ContactMethod + { + public string id { get; set; } + public Number Number { get; set; } + } + + public class Name4 + { + public string Value { get; set; } + } + + public class ContactInfo + { + public List ContactMethod { get; set; } + public Name4 Name { get; set; } + } + + public class UserInterface + { + public ContactInfo ContactInfo { get; set; } + } + + public class PIPPosition + { + public string Value { get; set; } + } + + public class ActiveSpeaker2 + { + public PIPPosition PIPPosition { get; set; } + } + + public class Connected2 + { + public string Value { get; set; } + } + + public class SignalState + { + public string Value { get; set; } + } + + public class SourceId + { + public string Value { get; set; } + } + + public class Type3 + { + public string Value { get; set; } + } + + public class Connector + { + public string id { get; set; } + public Connected2 Connected { get; set; } + public SignalState SignalState { get; set; } + public SourceId SourceId { get; set; } + public Type3 Type { get; set; } + } + + public class MainVideoSource + { + public string Value { get; set; } + } + + public class ConnectorId + { + public string Value { get; set; } + } + + public class FormatStatus + { + public string Value { get; set; } + } + + public class FormatType + { + public string Value { get; set; } + } + + public class MediaChannelId + { + public string Value { get; set; } + } + + public class Height + { + public string Value { get; set; } + } + + public class RefreshRate + { + public string Value { get; set; } + } + + public class Width + { + public string Value { get; set; } + } + + public class Resolution + { + public Height Height { get; set; } + public RefreshRate RefreshRate { get; set; } + public Width Width { get; set; } + } + + public class Source + { + public string id { get; set; } + public ConnectorId ConnectorId { get; set; } + public FormatStatus FormatStatus { get; set; } + public FormatType FormatType { get; set; } + public MediaChannelId MediaChannelId { get; set; } + public Resolution Resolution { get; set; } + } + + public class Input2 + { + public List Connector { get; set; } + public MainVideoSource MainVideoSource { get; set; } + public List Source { get; set; } + } + + public class Local : ValueProperty + { + string _Value; + + public string Value // Valid values are On/Off + { + get + { + return _Value; + } + set + { + _Value = value; + OnValueChanged(); + } + } + } + + public class LayoutFamily + { + public Local Local { get; set; } + + public LayoutFamily() + { + Local = new Local(); + } + } + + public class Layout + { + public LayoutFamily LayoutFamily { get; set; } + + public Layout() + { + LayoutFamily = new LayoutFamily(); + } + } + + public class Monitors + { + public string Value { get; set; } + } + + public class Connected3 + { + public string Value { get; set; } + } + + public class Name5 + { + public string Value { get; set; } + } + + public class PreferredFormat + { + public string Value { get; set; } + } + + public class ConnectedDevice2 + { + public Name5 Name { get; set; } + public PreferredFormat PreferredFormat { get; set; } + } + + public class MonitorRole + { + public string Value { get; set; } + } + + public class Height2 + { + public string Value { get; set; } + } + + public class RefreshRate2 + { + public string Value { get; set; } + } + + public class Width2 + { + public string Value { get; set; } + } + + public class Resolution2 + { + public Height2 Height { get; set; } + public RefreshRate2 RefreshRate { get; set; } + public Width2 Width { get; set; } + } + + public class Type4 + { + public string Value { get; set; } + } + + public class Connector2 + { + public string id { get; set; } + public Connected3 Connected { get; set; } + public ConnectedDevice2 ConnectedDevice { get; set; } + public MonitorRole MonitorRole { get; set; } + public Resolution2 Resolution { get; set; } + public Type4 Type { get; set; } + } + + public class Output2 + { + public List Connector { get; set; } + } + + public class PIPPosition2 + { + public string Value { get; set; } + } + + public class Presentation2 + { + public PIPPosition2 PIPPosition { get; set; } + } + + public class FullscreenMode + { + public string Value { get; set; } + } + + public class Mode8 : ValueProperty + { + public bool BoolValue { get; private set; } + + public string Value // Valid values are On/Off + { + set + { + // If the incoming value is "On" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "On"; + OnValueChanged(); + } + } + } + + + public class OnMonitorRole + { + public string Value { get; set; } + } + + public class PIPPosition3 : ValueProperty + { + string _Value; + + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + OnValueChanged(); + } + } + } + + public class Selfview + { + public FullscreenMode FullscreenMode { get; set; } + public Mode8 Mode { get; set; } + public OnMonitorRole OnMonitorRole { get; set; } + public PIPPosition3 PIPPosition { get; set; } + + public Selfview() + { + Mode = new Mode8(); + PIPPosition = new PIPPosition3(); + } + } + + public class Video + { + public ActiveSpeaker2 ActiveSpeaker { get; set; } + public Input2 Input { get; set; } + public Layout Layout { get; set; } + public Monitors Monitors { get; set; } + public Output2 Output { get; set; } + public Presentation2 Presentation { get; set; } + public Selfview Selfview { get; set; } + + public Video() + { + Selfview = new Selfview(); + Layout = new Layout(); + } + } + + public class AnswerState + { + public string Value { get; set; } + } + + public class CallType + { + public string Value { get; set; } + } + + public class CallbackNumber + { + public string Value { get; set; } + } + + public class DeviceType + { + public string Value { get; set; } + } + + public class Direction + { + public string Value { get; set; } + } + + public class Duration + { + public string Value { get; set; } + } + + public class FacilityServiceId + { + public string Value { get; set; } + } + + public class HoldReason + { + public string Value { get; set; } + } + + public class PlacedOnHold + { + public string Value { get; set; } + } + + public class Protocol + { + public string Value { get; set; } + } + + public class ReceiveCallRate + { + public string Value { get; set; } + } + + public class RemoteNumber + { + public string Value { get; set; } + } + + public class TransmitCallRate + { + public string Value { get; set; } + } + + public class Call + { + public string id { get; set; } + public AnswerState AnswerState { get; set; } + public CallType CallType { get; set; } + public CallbackNumber CallbackNumber { get; set; } + public DeviceType DeviceType { get; set; } + public Direction Direction { get; set; } + public DisplayName DisplayName { get; set; } + public Duration Duration { get; set; } + public Encryption Encryption { get; set; } + public FacilityServiceId FacilityServiceId { get; set; } + public string ghost { get; set; } + public HoldReason HoldReason { get; set; } + public PlacedOnHold PlacedOnHold { get; set; } + public Protocol Protocol { get; set; } + public ReceiveCallRate ReceiveCallRate { get; set; } + public RemoteNumber RemoteNumber { get; set; } + public Status2 Status { get; set; } + public TransmitCallRate TransmitCallRate { get; set; } + + public Call() + { + CallType = new CallType(); + Status = new Status2(); + } + } + + public class Type5 + { + public string Value { get; set; } + } + + public class Description2 : ValueProperty + { + string _Value; + + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + OnValueChanged(); + } + } + } + + public class Defined : ValueProperty + { + public bool BoolValue { get; private set; } + + public string Value // Valid values are True/False + { + set + { + // If the incoming value is "True" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "True"; + OnValueChanged(); + } + } + } + + public class RoomPreset + { + public string id { get; set; } + public Defined Defined { get; set; } + public Description2 Description { get; set; } + public Type5 Type { get; set; } + + public RoomPreset() + { + Defined = new Defined(); + Description = new Description2(); + Type = new Type5(); + } + } + + + + public class Status + { + public Audio Audio { get; set; } + public Bookings Bookings { get; set; } + public List Call { get; set; } + public Cameras Cameras { get; set; } + public Capabilities2 Capabilities { get; set; } + public Conference2 Conference { get; set; } + public Diagnostics Diagnostics { get; set; } + public Experimental Experimental { get; set; } + public H323 H323 { get; set; } + public List HttpFeedback { get; set; } + public MediaChannels MediaChannels { get; set; } + public List Network { get; set; } + public NetworkServices NetworkServices { get; set; } + public Peripherals Peripherals { get; set; } + public Provisioning Provisioning { get; set; } + public Proximity Proximity { get; set; } + public RoomAnalytics RoomAnalytics { get; set; } + + public List RoomPreset { get; set; } + + public SIP SIP { get; set; } + public Security Security { get; set; } + public Standby Standby { get; set; } + public SystemUnit SystemUnit { get; set; } + public Time Time { get; set; } + public UserInterface UserInterface { get; set; } + public Video Video { get; set; } + + public Status() + { + Audio = new Audio(); + Call = new List(); + Standby = new Standby(); + Cameras = new Cameras(); + RoomAnalytics = new RoomAnalytics(); + RoomPreset = new List(); + Conference = new Conference2(); + SystemUnit = new SystemUnit(); + Video = new Video(); + } + } + + public class RootObject + { + public Status Status { get; set; } + + public RootObject() + { + Status = new Status(); + } + } + } +} diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatusSparkPlus.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatusSparkPlus.cs new file mode 100644 index 00000000..9f7bf0b6 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatusSparkPlus.cs @@ -0,0 +1,1552 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.CiscoCodec +{ + public class xStatusSparkPlus + { + public class ConnectionStatus + { + public string Value { get; set; } + } + + public class EcReferenceDelay + { + public string Value { get; set; } + } + + public class Microphone + { + public string id { get; set; } + public ConnectionStatus ConnectionStatus { get; set; } + public EcReferenceDelay EcReferenceDelay { get; set; } + } + + public class Connectors + { + public List Microphone { get; set; } + } + + public class Input + { + public Connectors Connectors { get; set; } + } + + public class Mute + { + public string Value { get; set; } + } + + public class Microphones + { + public Mute Mute { get; set; } + } + + public class ConnectionStatus2 + { + public string Value { get; set; } + } + + public class DelayMs + { + public string Value { get; set; } + } + + public class Line + { + public string id { get; set; } + public ConnectionStatus2 ConnectionStatus { get; set; } + public DelayMs DelayMs { get; set; } + } + + public class Connectors2 + { + public List Line { get; set; } + } + + public class Output + { + public Connectors2 Connectors { get; set; } + } + + public class Volume + { + public string Value { get; set; } + } + + public class VolumeMute + { + public string Value { get; set; } + } + + public class Audio + { + public Input Input { get; set; } + public Microphones Microphones { get; set; } + public Output Output { get; set; } + public Volume Volume { get; set; } + public VolumeMute VolumeMute { get; set; } + } + + public class Id + { + public string Value { get; set; } + } + + public class Current + { + public Id Id { get; set; } + } + + public class Bookings + { + public Current Current { get; set; } + } + + public class Options + { + public string Value { get; set; } + } + + public class Capabilities + { + public Options Options { get; set; } + } + + public class Connected + { + public string Value { get; set; } + } + + public class Flip + { + public string Value { get; set; } + } + + public class HardwareID + { + public string Value { get; set; } + } + + public class MacAddress + { + public string Value { get; set; } + } + + public class Manufacturer + { + public string Value { get; set; } + } + + public class Model + { + public string Value { get; set; } + } + + public class Pan + { + public string Value { get; set; } + } + + public class Tilt + { + public string Value { get; set; } + } + + public class Zoom + { + public string Value { get; set; } + } + + public class Position + { + public Pan Pan { get; set; } + public Tilt Tilt { get; set; } + public Zoom Zoom { get; set; } + } + + public class SerialNumber + { + public string Value { get; set; } + } + + public class SoftwareID + { + public string Value { get; set; } + } + + public class Camera + { + public string id { get; set; } + public Capabilities Capabilities { get; set; } + public Connected Connected { get; set; } + public Flip Flip { get; set; } + public HardwareID HardwareID { get; set; } + public MacAddress MacAddress { get; set; } + public Manufacturer Manufacturer { get; set; } + public Model Model { get; set; } + public Position Position { get; set; } + public SerialNumber SerialNumber { get; set; } + public SoftwareID SoftwareID { get; set; } + } + + public class Availability + { + public string Value { get; set; } + } + + public class Status2 + { + public string Value { get; set; } + } + + public class SpeakerTrack + { + public Availability Availability { get; set; } + public Status2 Status { get; set; } + } + + public class Cameras + { + public List Camera { get; set; } + public SpeakerTrack SpeakerTrack { get; set; } + } + + public class MaxActiveCalls + { + public string Value { get; set; } + } + + public class MaxAudioCalls + { + public string Value { get; set; } + } + + public class MaxCalls + { + public string Value { get; set; } + } + + public class MaxVideoCalls + { + public string Value { get; set; } + } + + public class Conference + { + public MaxActiveCalls MaxActiveCalls { get; set; } + public MaxAudioCalls MaxAudioCalls { get; set; } + public MaxCalls MaxCalls { get; set; } + public MaxVideoCalls MaxVideoCalls { get; set; } + } + + public class Capabilities2 + { + public Conference Conference { get; set; } + } + + public class CallId + { + public string Value { get; set; } + } + + public class ActiveSpeaker + { + public CallId CallId { get; set; } + } + + public class DoNotDisturb + { + public string Value { get; set; } + } + + public class Mode + { + public string Value { get; set; } + } + + public class Line2 + { + public string id { get; set; } + public Mode Mode { get; set; } + } + + public class Mode2 + { + public string Value { get; set; } + } + + public class Multipoint + { + public Mode2 Mode { get; set; } + } + + public class CallId2 + { + public string Value { get; set; } + } + + public class SendingMode + { + public string Value { get; set; } + } + + public class Source + { + public string Value { get; set; } + } + + public class LocalInstance + { + public string id { get; set; } + public SendingMode SendingMode { get; set; } + public Source Source { get; set; } + } + + public class Mode3 + { + public string Value { get; set; } + } + + public class Mode4 + { + public string Value { get; set; } + } + + public class ReleaseFloorAvailability + { + public string Value { get; set; } + } + + public class RequestFloorAvailability + { + public string Value { get; set; } + } + + public class Whiteboard + { + public Mode4 Mode { get; set; } + public ReleaseFloorAvailability ReleaseFloorAvailability { get; set; } + public RequestFloorAvailability RequestFloorAvailability { get; set; } + } + + public class Presentation + { + public CallId2 CallId { get; set; } + public List LocalInstance { get; set; } + public Mode3 Mode { get; set; } + public Whiteboard Whiteboard { get; set; } + } + + public class CallId3 + { + public string Value { get; set; } + } + + public class Mode5 + { + public string Value { get; set; } + } + + public class SpeakerLock + { + public CallId3 CallId { get; set; } + public Mode5 Mode { get; set; } + } + + public class Conference2 + { + public ActiveSpeaker ActiveSpeaker { get; set; } + public DoNotDisturb DoNotDisturb { get; set; } + public List Line { get; set; } + public Multipoint Multipoint { get; set; } + public Presentation Presentation { get; set; } + public SpeakerLock SpeakerLock { get; set; } + } + + public class Conference3 + { + } + + public class Experimental + { + public Conference3 Conference { get; set; } + } + + public class Address + { + public string Value { get; set; } + } + + public class Port + { + public string Value { get; set; } + } + + public class Reason + { + public string Value { get; set; } + } + + public class Status3 + { + public string Value { get; set; } + } + + public class Gatekeeper + { + public Address Address { get; set; } + public Port Port { get; set; } + public Reason Reason { get; set; } + public Status3 Status { get; set; } + } + + public class Reason2 + { + public string Value { get; set; } + } + + public class Status4 + { + public string Value { get; set; } + } + + public class Mode6 + { + public Reason2 Reason { get; set; } + public Status4 Status { get; set; } + } + + public class H323 + { + public Gatekeeper Gatekeeper { get; set; } + public Mode6 Mode { get; set; } + } + + public class Expression + { + public string id { get; set; } + public string Value { get; set; } + } + + public class Format + { + public string Value { get; set; } + } + + public class URL + { + public string Value { get; set; } + } + + public class HttpFeedback + { + public string id { get; set; } + public List Expression { get; set; } + public Format Format { get; set; } + public URL URL { get; set; } + } + + public class MediaChannels + { + } + + public class Address2 + { + public string Value { get; set; } + } + + public class Capabilities3 + { + public string Value { get; set; } + } + + public class DeviceId + { + public string Value { get; set; } + } + + public class Duplex + { + public string Value { get; set; } + } + + public class Platform + { + public string Value { get; set; } + } + + public class PortID + { + public string Value { get; set; } + } + + public class PrimaryMgmtAddress + { + public string Value { get; set; } + } + + public class SysName + { + public string Value { get; set; } + } + + public class SysObjectID + { + public string Value { get; set; } + } + + public class VTPMgmtDomain + { + public string Value { get; set; } + } + + public class Version + { + public string Value { get; set; } + } + + public class VoIPApplianceVlanID + { + public string Value { get; set; } + } + + public class CDP + { + public Address2 Address { get; set; } + public Capabilities3 Capabilities { get; set; } + public DeviceId DeviceId { get; set; } + public Duplex Duplex { get; set; } + public Platform Platform { get; set; } + public PortID PortID { get; set; } + public PrimaryMgmtAddress PrimaryMgmtAddress { get; set; } + public SysName SysName { get; set; } + public SysObjectID SysObjectID { get; set; } + public VTPMgmtDomain VTPMgmtDomain { get; set; } + public Version Version { get; set; } + public VoIPApplianceVlanID VoIPApplianceVlanID { get; set; } + } + + public class Name + { + public string Value { get; set; } + } + + public class Domain + { + public Name Name { get; set; } + } + + public class Address3 + { + public string Value { get; set; } + } + + public class Server + { + public string id { get; set; } + public Address3 Address { get; set; } + } + + public class DNS + { + public Domain Domain { get; set; } + public List Server { get; set; } + } + + public class MacAddress2 + { + public string Value { get; set; } + } + + public class Speed + { + public string Value { get; set; } + } + + public class Ethernet + { + public MacAddress2 MacAddress { get; set; } + public Speed Speed { get; set; } + } + + public class Address4 + { + public string Value { get; set; } + } + + public class Gateway + { + public string Value { get; set; } + } + + public class SubnetMask + { + public string Value { get; set; } + } + + public class IPv4 + { + public Address4 Address { get; set; } + public Gateway Gateway { get; set; } + public SubnetMask SubnetMask { get; set; } + } + + public class Address5 + { + public string Value { get; set; } + } + + public class Gateway2 + { + public string Value { get; set; } + } + + public class IPv6 + { + public Address5 Address { get; set; } + public Gateway2 Gateway { get; set; } + } + + public class VlanId + { + public string Value { get; set; } + } + + public class Voice + { + public VlanId VlanId { get; set; } + } + + public class VLAN + { + public Voice Voice { get; set; } + } + + public class Network + { + public string id { get; set; } + public CDP CDP { get; set; } + public DNS DNS { get; set; } + public Ethernet Ethernet { get; set; } + public IPv4 IPv4 { get; set; } + public IPv6 IPv6 { get; set; } + public VLAN VLAN { get; set; } + } + + public class CurrentAddress + { + public string Value { get; set; } + } + + public class Address6 + { + public string Value { get; set; } + } + + public class Server2 + { + public string id { get; set; } + public Address6 Address { get; set; } + } + + public class Status5 + { + public string Value { get; set; } + } + + public class NTP + { + public CurrentAddress CurrentAddress { get; set; } + public List Server { get; set; } + public Status5 Status { get; set; } + } + + public class NetworkServices + { + public NTP NTP { get; set; } + } + + public class HardwareInfo + { + public string Value { get; set; } + } + + public class ID2 + { + public string Value { get; set; } + } + + public class Name2 + { + public string Value { get; set; } + } + + public class SoftwareInfo + { + public string Value { get; set; } + } + + public class Status6 + { + public string Value { get; set; } + } + + public class Type + { + public string Value { get; set; } + } + + public class UpgradeStatus + { + public string Value { get; set; } + } + + public class ConnectedDevice + { + public string id { get; set; } + public HardwareInfo HardwareInfo { get; set; } + public ID2 ID { get; set; } + public Name2 Name { get; set; } + public SoftwareInfo SoftwareInfo { get; set; } + public Status6 Status { get; set; } + public Type Type { get; set; } + public UpgradeStatus UpgradeStatus { get; set; } + } + + public class Peripherals + { + public List ConnectedDevice { get; set; } + } + + public class Enabled + { + public string Value { get; set; } + } + + public class LastLoggedInUserId + { + public string Value { get; set; } + } + + public class LoggedIn + { + public string Value { get; set; } + } + + public class ExtensionMobility + { + public Enabled Enabled { get; set; } + public LastLoggedInUserId LastLoggedInUserId { get; set; } + public LoggedIn LoggedIn { get; set; } + } + + public class CUCM + { + public ExtensionMobility ExtensionMobility { get; set; } + } + + public class CompletedAt + { + public string Value { get; set; } + } + + public class URL2 + { + public string Value { get; set; } + } + + public class VersionId + { + public string Value { get; set; } + } + + public class Current2 + { + public CompletedAt CompletedAt { get; set; } + public URL2 URL { get; set; } + public VersionId VersionId { get; set; } + } + + public class LastChange + { + public string Value { get; set; } + } + + public class Message + { + public string Value { get; set; } + } + + public class Phase + { + public string Value { get; set; } + } + + public class SessionId + { + public string Value { get; set; } + } + + public class Status7 + { + public string Value { get; set; } + } + + public class URL3 + { + public string Value { get; set; } + } + + public class VersionId2 + { + public string Value { get; set; } + } + + public class UpgradeStatus2 + { + public LastChange LastChange { get; set; } + public Message Message { get; set; } + public Phase Phase { get; set; } + public SessionId SessionId { get; set; } + public Status7 Status { get; set; } + public URL3 URL { get; set; } + public VersionId2 VersionId { get; set; } + } + + public class Software + { + public Current2 Current { get; set; } + public UpgradeStatus2 UpgradeStatus { get; set; } + } + + public class Status8 + { + public string Value { get; set; } + } + + public class Provisioning + { + public CUCM CUCM { get; set; } + public Software Software { get; set; } + public Status8 Status { get; set; } + } + + public class Availability2 + { + public string Value { get; set; } + } + + public class Services + { + public Availability2 Availability { get; set; } + } + + public class Proximity + { + public Services Services { get; set; } + } + + public class Current3 + { + public string Value { get; set; } + } + + public class PeopleCount + { + public Current3 Current { get; set; } + } + + public class PeoplePresence + { + public string Value { get; set; } + } + + public class RoomAnalytics + { + public PeopleCount PeopleCount { get; set; } + public PeoplePresence PeoplePresence { get; set; } + } + + public class URI + { + public string Value { get; set; } + } + + public class Primary + { + public URI URI { get; set; } + } + + public class AlternateURI + { + public Primary Primary { get; set; } + } + + public class Authentication + { + public string Value { get; set; } + } + + public class DisplayName + { + public string Value { get; set; } + } + + public class Mode7 + { + public string Value { get; set; } + } + + public class URI2 + { + public string Value { get; set; } + } + + public class CallForward + { + public DisplayName DisplayName { get; set; } + public Mode7 Mode { get; set; } + public URI2 URI { get; set; } + } + + public class MessagesWaiting + { + public string Value { get; set; } + } + + public class URI3 + { + public string Value { get; set; } + } + + public class Mailbox + { + public MessagesWaiting MessagesWaiting { get; set; } + public URI3 URI { get; set; } + } + + public class Address7 + { + public string Value { get; set; } + } + + public class Status9 + { + public string Value { get; set; } + } + + public class Proxy + { + public string id { get; set; } + public Address7 Address { get; set; } + public Status9 Status { get; set; } + } + + public class Reason3 + { + public string Value { get; set; } + } + + public class Status10 + { + public string Value { get; set; } + } + + public class URI4 + { + public string Value { get; set; } + } + + public class Registration + { + public string id { get; set; } + public Reason3 Reason { get; set; } + public Status10 Status { get; set; } + public URI4 URI { get; set; } + } + + public class Secure + { + public string Value { get; set; } + } + + public class Verified + { + public string Value { get; set; } + } + + public class SIP + { + public AlternateURI AlternateURI { get; set; } + public Authentication Authentication { get; set; } + public CallForward CallForward { get; set; } + public Mailbox Mailbox { get; set; } + public List Proxy { get; set; } + public List Registration { get; set; } + public Secure Secure { get; set; } + public Verified Verified { get; set; } + } + + public class Mode8 + { + public string Value { get; set; } + } + + public class FIPS + { + public Mode8 Mode { get; set; } + } + + public class CallHistory + { + public string Value { get; set; } + } + + public class Configurations + { + public string Value { get; set; } + } + + public class DHCP + { + public string Value { get; set; } + } + + public class InternalLogging + { + public string Value { get; set; } + } + + public class LocalPhonebook + { + public string Value { get; set; } + } + + public class Persistency + { + public CallHistory CallHistory { get; set; } + public Configurations Configurations { get; set; } + public DHCP DHCP { get; set; } + public InternalLogging InternalLogging { get; set; } + public LocalPhonebook LocalPhonebook { get; set; } + } + + public class Security + { + public FIPS FIPS { get; set; } + public Persistency Persistency { get; set; } + } + + public class State + { + public string Value { get; set; } + } + + public class Standby + { + public State State { get; set; } + } + + public class CompatibilityLevel + { + public string Value { get; set; } + } + + public class SerialNumber2 + { + public string Value { get; set; } + } + + public class Module + { + public CompatibilityLevel CompatibilityLevel { get; set; } + public SerialNumber2 SerialNumber { get; set; } + } + + public class Hardware + { + public Module Module { get; set; } + } + + public class ProductId + { + public string Value { get; set; } + } + + public class ProductPlatform + { + public string Value { get; set; } + } + + public class ProductType + { + public string Value { get; set; } + } + + public class DisplayName2 + { + public string Value { get; set; } + } + + public class Name3 + { + public string Value { get; set; } + } + + public class Encryption + { + public string Value { get; set; } + } + + public class MultiSite + { + public string Value { get; set; } + } + + public class RemoteMonitoring + { + public string Value { get; set; } + } + + public class OptionKeys + { + public Encryption Encryption { get; set; } + public MultiSite MultiSite { get; set; } + public RemoteMonitoring RemoteMonitoring { get; set; } + } + + public class ReleaseDate + { + public string Value { get; set; } + } + + public class Version2 + { + public string Value { get; set; } + } + + public class Software2 + { + public DisplayName2 DisplayName { get; set; } + public Name3 Name { get; set; } + public OptionKeys OptionKeys { get; set; } + public ReleaseDate ReleaseDate { get; set; } + public Version2 Version { get; set; } + } + + public class NumberOfActiveCalls + { + public string Value { get; set; } + } + + public class NumberOfInProgressCalls + { + public string Value { get; set; } + } + + public class NumberOfSuspendedCalls + { + public string Value { get; set; } + } + + public class State2 + { + public NumberOfActiveCalls NumberOfActiveCalls { get; set; } + public NumberOfInProgressCalls NumberOfInProgressCalls { get; set; } + public NumberOfSuspendedCalls NumberOfSuspendedCalls { get; set; } + } + + public class Uptime + { + public string Value { get; set; } + } + + public class SystemUnit + { + public Hardware Hardware { get; set; } + public ProductId ProductId { get; set; } + public ProductPlatform ProductPlatform { get; set; } + public ProductType ProductType { get; set; } + public Software2 Software { get; set; } + public State2 State { get; set; } + public Uptime Uptime { get; set; } + } + + public class SystemTime + { + public DateTime Value { get; set; } + } + + public class Time + { + public SystemTime SystemTime { get; set; } + } + + public class Number + { + public string Value { get; set; } + } + + public class ContactMethod + { + public string id { get; set; } + public Number Number { get; set; } + } + + public class Name4 + { + public string Value { get; set; } + } + + public class ContactInfo + { + public List ContactMethod { get; set; } + public Name4 Name { get; set; } + } + + public class UserInterface + { + public ContactInfo ContactInfo { get; set; } + } + + public class PIPPosition + { + public string Value { get; set; } + } + + public class ActiveSpeaker2 + { + public PIPPosition PIPPosition { get; set; } + } + + public class Connected2 + { + public string Value { get; set; } + } + + public class DeviceType + { + public string Value { get; set; } + } + + public class Name5 + { + public string Value { get; set; } + } + + public class PowerStatus + { + public string Value { get; set; } + } + + public class VendorId + { + public string Value { get; set; } + } + + public class CEC + { + public string id { get; set; } + public DeviceType DeviceType { get; set; } + public Name5 Name { get; set; } + public PowerStatus PowerStatus { get; set; } + public VendorId VendorId { get; set; } + } + + public class ConnectedDevice2 + { + public List CEC { get; set; } + } + + public class SignalState + { + public string Value { get; set; } + } + + public class SourceId + { + public string Value { get; set; } + } + + public class Type2 + { + public string Value { get; set; } + } + + public class Connector + { + public string id { get; set; } + public Connected2 Connected { get; set; } + public ConnectedDevice2 ConnectedDevice { get; set; } + public SignalState SignalState { get; set; } + public SourceId SourceId { get; set; } + public Type2 Type { get; set; } + } + + public class MainVideoSource + { + public string Value { get; set; } + } + + public class ConnectorId + { + public string Value { get; set; } + } + + public class FormatStatus + { + public string Value { get; set; } + } + + public class FormatType + { + public string Value { get; set; } + } + + public class MediaChannelId + { + public string Value { get; set; } + } + + public class Height + { + public string Value { get; set; } + } + + public class RefreshRate + { + public string Value { get; set; } + } + + public class Width + { + public string Value { get; set; } + } + + public class Resolution + { + public Height Height { get; set; } + public RefreshRate RefreshRate { get; set; } + public Width Width { get; set; } + } + + public class Source2 + { + public string id { get; set; } + public ConnectorId ConnectorId { get; set; } + public FormatStatus FormatStatus { get; set; } + public FormatType FormatType { get; set; } + public MediaChannelId MediaChannelId { get; set; } + public Resolution Resolution { get; set; } + } + + public class Input2 + { + public List Connector { get; set; } + public MainVideoSource MainVideoSource { get; set; } + public List Source { get; set; } + } + + public class Local + { + public string Value { get; set; } + } + + public class LayoutFamily + { + public Local Local { get; set; } + } + + public class Layout + { + public LayoutFamily LayoutFamily { get; set; } + } + + public class Monitors + { + public string Value { get; set; } + } + + public class Connected3 + { + public string Value { get; set; } + } + + public class DeviceType2 + { + public string Value { get; set; } + } + + public class Name6 + { + public string Value { get; set; } + } + + public class PowerStatus2 + { + public string Value { get; set; } + } + + public class VendorId2 + { + public string Value { get; set; } + } + + public class CEC2 + { + public string id { get; set; } + public DeviceType2 DeviceType { get; set; } + public Name6 Name { get; set; } + public PowerStatus2 PowerStatus { get; set; } + public VendorId2 VendorId { get; set; } + } + + public class Name7 + { + public string Value { get; set; } + } + + public class PreferredFormat + { + public string Value { get; set; } + } + + public class ConnectedDevice3 + { + public List CEC { get; set; } + public Name7 Name { get; set; } + public PreferredFormat PreferredFormat { get; set; } + } + + public class MonitorRole + { + public string Value { get; set; } + } + + public class Height2 + { + public string Value { get; set; } + } + + public class RefreshRate2 + { + public string Value { get; set; } + } + + public class Width2 + { + public string Value { get; set; } + } + + public class Resolution2 + { + public Height2 Height { get; set; } + public RefreshRate2 RefreshRate { get; set; } + public Width2 Width { get; set; } + } + + public class Type3 + { + public string Value { get; set; } + } + + public class Connector2 + { + public string id { get; set; } + public Connected3 Connected { get; set; } + public ConnectedDevice3 ConnectedDevice { get; set; } + public MonitorRole MonitorRole { get; set; } + public Resolution2 Resolution { get; set; } + public Type3 Type { get; set; } + } + + public class Output2 + { + public List Connector { get; set; } + } + + public class PIPPosition2 + { + public string Value { get; set; } + } + + public class Presentation2 + { + public PIPPosition2 PIPPosition { get; set; } + } + + public class FullscreenMode + { + public string Value { get; set; } + } + + public class Mode9 + { + public string Value { get; set; } + } + + public class OnMonitorRole + { + public string Value { get; set; } + } + + public class PIPPosition3 + { + public string Value { get; set; } + } + + public class Selfview + { + public FullscreenMode FullscreenMode { get; set; } + public Mode9 Mode { get; set; } + public OnMonitorRole OnMonitorRole { get; set; } + public PIPPosition3 PIPPosition { get; set; } + } + + public class Video + { + public ActiveSpeaker2 ActiveSpeaker { get; set; } + public Input2 Input { get; set; } + public Layout Layout { get; set; } + public Monitors Monitors { get; set; } + public Output2 Output { get; set; } + public Presentation2 Presentation { get; set; } + public Selfview Selfview { get; set; } + } + + public class Status + { + public Audio Audio { get; set; } + public Bookings Bookings { get; set; } + public Cameras Cameras { get; set; } + public Capabilities2 Capabilities { get; set; } + public Conference2 Conference { get; set; } + public Experimental Experimental { get; set; } + public H323 H323 { get; set; } + public List HttpFeedback { get; set; } + public MediaChannels MediaChannels { get; set; } + public List Network { get; set; } + public NetworkServices NetworkServices { get; set; } + public Peripherals Peripherals { get; set; } + public Provisioning Provisioning { get; set; } + public Proximity Proximity { get; set; } + public RoomAnalytics RoomAnalytics { get; set; } + public SIP SIP { get; set; } + public Security Security { get; set; } + public Standby Standby { get; set; } + public SystemUnit SystemUnit { get; set; } + public Time Time { get; set; } + public UserInterface UserInterface { get; set; } + public Video Video { get; set; } + } + + public class RootObject + { + public Status Status { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CodecActiveCallItem.cs.orig b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CodecActiveCallItem.cs.orig new file mode 100644 index 00000000..4b47d127 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CodecActiveCallItem.cs.orig @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec + +{ + public class CodecActiveCallItem + { + public string Name { get; set; } + + public string Number { get; set; } + +<<<<<<< HEAD + public eCodecCallType Type { get; private set; } + + public CodecActiveCallItem(string name, string number, eCodecCallType type) + { + Name = name; + Number = number; + Type = type; + } +======= + public eCodecCallType Type { get; set; } + + public eCodecCallStatus Status { get; set; } + + public string Id { get; set; } +>>>>>>> origin/feature/cisco-spark-2 + } + + public enum eCodecCallType + { + None, Audio, Video + } + + public enum eCodecCallStatus + { + Dialing, Established, Incoming + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/CameraControl.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/CameraControl.cs new file mode 100644 index 00000000..b17bd325 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/CameraControl.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + public enum eCameraControlMode + { + Off = 0, + Manual, + Auto + } + + + public interface IHasCameras + { + event EventHandler CameraSelected; + + List Cameras { get; } + + CameraBase SelectedCamera { get; } + + StringFeedback SelectedCameraFeedback { get; } + + void SelectCamera(string key); + } + + /// + /// Aggregates far end cameras with near end cameras + /// + public interface IHasCodecCameras : IHasCameras, IHasFarEndCameraControl + { + + } + + /// + /// To be implmented on codecs that can disable their camera(s) to blank the near end video + /// + public interface IHasCameraOff + { + BoolFeedback CameraIsOffFeedback { get; } + void CameraOff(); + } + + public class CameraSelectedEventArgs : EventArgs + { + public CameraBase SelectedCamera { get; private set; } + + public CameraSelectedEventArgs(CameraBase camera) + { + SelectedCamera = camera; + } + } + + public interface IHasFarEndCameraControl + { + CameraBase FarEndCamera { get; } + + BoolFeedback ControllingFarEndCameraFeedback { get; } + + } + + /// + /// Used to decorate a camera as a far end + /// + public interface IAmFarEndCamera + { + + } + + /// + /// Aggregates the pan, tilt and zoom interfaces + /// + public interface IHasCameraPtzControl : IHasCameraPanControl, IHasCameraTiltControl, IHasCameraZoomControl + { + /// + /// Resets the camera position + /// + void PositionHome(); + } + + /// + /// Interface for camera pan control + /// + public interface IHasCameraPanControl + { + void PanLeft(); + void PanRight(); + void PanStop(); + } + + /// + /// Interface for camera tilt control + /// + public interface IHasCameraTiltControl + { + void TiltDown(); + void TiltUp(); + void TiltStop(); + } + + /// + /// Interface for camera zoom control + /// + public interface IHasCameraZoomControl + { + void ZoomIn(); + void ZoomOut(); + void ZoomStop(); + } + + /// + /// Interface for camera focus control + /// + public interface IHasCameraFocusControl + { + void FocusNear(); + void FocusFar(); + void FocusStop(); + + void TriggerAutoFocus(); + } + + public interface IHasCameraAutoMode + { + void CameraAutoModeOn(); + void CameraAutoModeOff(); + void CameraAutoModeToggle(); + BoolFeedback CameraAutoModeIsOnFeedback { get; } + } + + + + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasCodecLayouts.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasCodecLayouts.cs new file mode 100644 index 00000000..131d610f --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasCodecLayouts.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + /// + /// Defines the required elements for layout control + /// + public interface IHasCodecLayouts + { + StringFeedback LocalLayoutFeedback { get; } + + void LocalLayoutToggle(); + void LocalLayoutToggleSingleProminent(); + void MinMaxLayoutToggle(); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasCodecSelfview.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasCodecSelfview.cs new file mode 100644 index 00000000..3b079b7d --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasCodecSelfview.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + /// + /// Defines the requred elements for selfview control + /// + public interface IHasCodecSelfView + { + BoolFeedback SelfviewIsOnFeedback { get; } + + bool ShowSelfViewByDefault { get; } + + void SelfViewModeOn(); + + void SelfViewModeOff(); + + void SelfViewModeToggle(); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasVideoCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasVideoCodec.cs new file mode 100644 index 00000000..1d0f4850 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasVideoCodec.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + /// + /// For rooms that have video codec + /// + public interface IHasVideoCodec + { + VideoCodecBase VideoCodec { get; } + BoolFeedback InCallFeedback { get; } + + ///// + ///// Make this more specific + ///// + //List ActiveCalls { get; } + + /// + /// States: 0 for on hook, 1 for video, 2 for audio, 3 for telekenesis + /// + IntFeedback CallTypeFeedback { get; } + + /// + /// + /// + BoolFeedback PrivacyModeIsOnFeedback { get; } + + /// + /// When something in the room is sharing with the far end or through other means + /// + BoolFeedback IsSharingFeedback { get; } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/iVideoCodecInfo.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/iVideoCodecInfo.cs new file mode 100644 index 00000000..902dd68f --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/iVideoCodecInfo.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + /// + /// Implements a common set of data about a codec + /// + public interface iVideoCodecInfo + { + VideoCodecInfo CodecInfo { get; } + } + + /// + /// Stores general information about a codec + /// + public abstract class VideoCodecInfo + { + public abstract bool MultiSiteOptionIsEnabled { get; } + public abstract string IpAddress { get; } + public abstract string SipPhoneNumber { get; } + public abstract string E164Alias { get; } + public abstract string H323Id { get; } + public abstract string SipUri { get; } + public abstract bool AutoAnswerEnabled { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockCodecDirectory.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockCodecDirectory.cs new file mode 100644 index 00000000..41b70661 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockCodecDirectory.cs @@ -0,0 +1,419 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; + + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public static class MockVideoCodecDirectory + { + public enum eFolderId + { + UnitedStates, + Canada, + NewYork, + Boston, + SanFrancisco, + Denver, + Austin, + Calgary + } + + + /// + /// Aggregates the directory items for all directories into a single directory for searching purposes + /// + public static CodecDirectory CompleteDirectory + { + get + { + var completeDirectory = new CodecDirectory(); + + completeDirectory.AddContactsToDirectory(DirectoryRoot.CurrentDirectoryResults); + completeDirectory.AddContactsToDirectory(UnitedStatesFolderContents.CurrentDirectoryResults); + completeDirectory.AddContactsToDirectory(CanadaFolderContents.CurrentDirectoryResults); + completeDirectory.AddContactsToDirectory(NewYorkFolderContents.CurrentDirectoryResults); + completeDirectory.AddContactsToDirectory(BostonFolderContents.CurrentDirectoryResults); + completeDirectory.AddContactsToDirectory(DenverFolderContents.CurrentDirectoryResults); + completeDirectory.AddContactsToDirectory(AustinFolderContents.CurrentDirectoryResults); + completeDirectory.AddContactsToDirectory(CalgaryFolderContents.CurrentDirectoryResults); + + return completeDirectory; + } + } + + public static CodecDirectory DirectoryRoot + { + get + { + var directory = new CodecDirectory(); + + directory.AddFoldersToDirectory + ( + new List() + { + new DirectoryFolder() + { + FolderId = eFolderId.UnitedStates.ToString(), + Name = "United States", + ParentFolderId = "", + Contacts = null + }, + new DirectoryFolder() + { + FolderId = eFolderId.Canada.ToString(), + Name = "Canada", + ParentFolderId = "", + Contacts = null + } + } + ); + + directory.AddContactsToDirectory + ( + new List() + { + new DirectoryContact() + { + Name = "Corporate Bridge", + ContactMethods = new List() + { + new ContactMethod() + { + ContactMethodId = "c_1", + Number = "site.corp.com", + Device = eContactMethodDevice.Video, + CallType = eContactMethodCallType.Video + } + } + } + } + ); + + return directory; + } + } + + public static CodecDirectory UnitedStatesFolderContents + { + get + { + var directory = new CodecDirectory(); + + directory.ResultsFolderId = eFolderId.UnitedStates.ToString(); + directory.AddFoldersToDirectory + ( + new List() + { + new DirectoryFolder() + { + FolderId = eFolderId.NewYork.ToString(), + Name = "New York", + ParentFolderId = eFolderId.UnitedStates.ToString(), + Contacts = null + }, + new DirectoryFolder() + { + FolderId = eFolderId.Boston.ToString(), + Name = "Boston", + ParentFolderId = eFolderId.UnitedStates.ToString(), + Contacts = null + }, + new DirectoryFolder() + { + FolderId = eFolderId.SanFrancisco.ToString(), + Name = "San Francisco", + ParentFolderId = eFolderId.UnitedStates.ToString(), + Contacts = null + }, + new DirectoryFolder() + { + FolderId = eFolderId.Denver.ToString(), + Name = "Denver", + ParentFolderId = eFolderId.UnitedStates.ToString(), + Contacts = null + }, + new DirectoryFolder() + { + FolderId = eFolderId.Austin.ToString(), + Name = "Austin", + ParentFolderId = eFolderId.UnitedStates.ToString(), + Contacts = null + } + } + ); + + return directory; + } + } + + public static CodecDirectory NewYorkFolderContents + { + get + { + var directory = new CodecDirectory(); + + directory.ResultsFolderId = eFolderId.NewYork.ToString(); + directory.AddContactsToDirectory + ( + new List() + { + new DirectoryContact() + { + ContactId = "nyc_1", + Name = "Meeting Room", + Title = @"", + ContactMethods = new List() + { + new ContactMethod() + { + ContactMethodId = "cid_1", + Number = "nycmeetingroom.pepperdash.com", + Device = eContactMethodDevice.Video, + CallType = eContactMethodCallType.Video + } + } + }, + new DirectoryContact() + { + ContactId = "nyc_2", + Name = "Sumanth Rayancha", + Title = @"CTO", + ContactMethods = new List() + { + new ContactMethod() + { + ContactMethodId = "cid_1", + Number = "srayancha.pepperdash.com", + Device = eContactMethodDevice.Video, + CallType = eContactMethodCallType.Video + } + } + }, + new DirectoryContact() + { + ContactId = "nyc_3", + Name = "Justin Gordon", + Title = @"Software Developer", + ContactMethods = new List() + { + new ContactMethod() + { + ContactMethodId = "cid_1", + Number = "jgordon.pepperdash.com", + Device = eContactMethodDevice.Video, + CallType = eContactMethodCallType.Video + } + } + } + } + ); + + return directory; + } + } + + public static CodecDirectory BostonFolderContents + { + get + { + var directory = new CodecDirectory(); + + directory.ResultsFolderId = eFolderId.Boston.ToString(); + directory.AddContactsToDirectory + ( + new List() + { + new DirectoryContact() + { + ContactId = "bos_1", + Name = "Board Room", + Title = @"", + ContactMethods = new List() + { + new ContactMethod() + { + ContactMethodId = "cid_1", + Number = "bosboardroom.pepperdash.com", + Device = eContactMethodDevice.Video, + CallType = eContactMethodCallType.Video + } + } + } + } + ); + + return directory; + } + } + + public static CodecDirectory SanFranciscoFolderContents + { + get + { + var directory = new CodecDirectory(); + + directory.ResultsFolderId = eFolderId.SanFrancisco.ToString(); + directory.AddContactsToDirectory + ( + new List() + { + new DirectoryContact() + { + ContactId = "sfo_1", + Name = "David Huselid", + Title = @"Cive President, COO", + ContactMethods = new List() + { + new ContactMethod() + { + ContactMethodId = "cid_1", + Number = "dhuselid.pepperdash.com", + Device = eContactMethodDevice.Video, + CallType = eContactMethodCallType.Video + } + } + } + } + ); + + return directory; + } + } + + public static CodecDirectory DenverFolderContents + { + get + { + var directory = new CodecDirectory(); + + directory.ResultsFolderId = eFolderId.Denver.ToString(); + directory.AddContactsToDirectory + ( + new List() + { + new DirectoryContact() + { + ContactId = "den_1", + Name = "Heath Volmer", + Title = @"Software Developer", + ContactMethods = new List() + { + new ContactMethod() + { + ContactMethodId = "cid_1", + Number = "hvolmer.pepperdash.com", + Device = eContactMethodDevice.Video, + CallType = eContactMethodCallType.Video + } + } + } + } + ); + + return directory; + } + } + + public static CodecDirectory AustinFolderContents + { + get + { + var directory = new CodecDirectory(); + + directory.ResultsFolderId = eFolderId.Austin.ToString(); + directory.AddContactsToDirectory + ( + new List() + { + new DirectoryContact() + { + ContactId = "atx_1", + Name = "Vincent Longano", + Title = @"Product Development Manager", + ContactMethods = new List() + { + new ContactMethod() + { + ContactMethodId = "cid_1", + Number = "vlongano.pepperdash.com", + Device = eContactMethodDevice.Video, + CallType = eContactMethodCallType.Video + } + } + } + } + ); + + return directory; + } + } + + public static CodecDirectory CanadaFolderContents + { + get + { + var directory = new CodecDirectory(); + + directory.ResultsFolderId = eFolderId.Canada.ToString(); + directory.AddFoldersToDirectory + ( + new List() + { + new DirectoryFolder() + { + FolderId = eFolderId.Calgary.ToString(), + Name = "Calgary", + ParentFolderId = eFolderId.Canada.ToString(), + Contacts = null + } + } + ); + + return directory; + } + } + + public static CodecDirectory CalgaryFolderContents + { + get + { + var directory = new CodecDirectory(); + + directory.ResultsFolderId = eFolderId.Calgary.ToString(); + directory.AddContactsToDirectory + ( + new List() + { + new DirectoryContact() + { + ContactId = "cdn_1", + Name = "Neil Dorin", + Title = @"Software Developer /SC", + ContactMethods = new List() + { + new ContactMethod() + { + ContactMethodId = "cid_1", + Number = "ndorin@pepperdash.com", + Device = eContactMethodDevice.Video, + CallType = eContactMethodCallType.Video + } + } + } + } + ); + + return directory; + } + } + + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs new file mode 100644 index 00000000..386da4f6 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs @@ -0,0 +1,773 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.Cameras; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public class MockVC : VideoCodecBase, IRoutingSource, IHasCallHistory, IHasScheduleAwareness, IHasCallFavorites, IHasDirectory, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets + { + public MockVcPropertiesConfig PropertiesConfig; + + public RoutingInputPort CodecOsdIn { get; private set; } + public RoutingInputPort HdmiIn1 { get; private set; } + public RoutingInputPort HdmiIn2 { get; private set; } + public RoutingOutputPort HdmiOut { get; private set; } + + public CodecCallFavorites CallFavorites { get; private set; } + + /// + /// + /// + public MockVC(DeviceConfig config) + : base(config) + { + PropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString()); + + CodecInfo = new MockCodecInfo(); + + // Get favoritesw + if (PropertiesConfig.Favorites != null) + { + CallFavorites = new CodecCallFavorites(); + CallFavorites.Favorites = PropertiesConfig.Favorites; + } + + DirectoryBrowseHistory = new List(); + + // Debug helpers + MuteFeedback.OutputChange += (o, a) => Debug.Console(1, this, "Mute={0}", _IsMuted); + PrivacyModeIsOnFeedback.OutputChange += (o, a) => Debug.Console(1, this, "Privacy={0}", _PrivacyModeIsOn); + SharingSourceFeedback.OutputChange += (o, a) => Debug.Console(1, this, "SharingSource={0}", _SharingSource); + VolumeLevelFeedback.OutputChange += (o, a) => Debug.Console(1, this, "Volume={0}", _VolumeLevel); + + CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => DirectoryBrowseHistory.Count > 0); + + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 0, this); + InputPorts.Add(CodecOsdIn); + HdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 1, this); + InputPorts.Add(HdmiIn1); + HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, 2, this); + InputPorts.Add(HdmiIn2); + HdmiOut = new RoutingOutputPort(RoutingPortNames.HdmiOut, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this); + OutputPorts.Add(HdmiOut); + + CallHistory = new CodecCallHistory(); + for (int i = 0; i < 10; i++) + { + var call = new CodecCallHistory.CallHistoryEntry(); + call.Name = "Call " + i; + call.Number = i + "@call.com"; + CallHistory.RecentCalls.Add(call); + } + // eventually fire history event here + + SetupCameras(); + + SetIsReady(); + } + + protected override Func MuteFeedbackFunc + { + get { return () => _IsMuted; } + } + bool _IsMuted; + + protected override Func PrivacyModeIsOnFeedbackFunc + { + get { return () => _PrivacyModeIsOn; } + } + bool _PrivacyModeIsOn; + + protected override Func SharingSourceFeedbackFunc + { + get { return () => _SharingSource; } + } + string _SharingSource; + + protected override Func SharingContentIsOnFeedbackFunc + { + get { return () => _SharingIsOn; } + } + bool _SharingIsOn; + + protected override Func VolumeLevelFeedbackFunc + { + get { return () => _VolumeLevel; } + } + int _VolumeLevel; + + protected override Func StandbyIsOnFeedbackFunc + { + get { return () => _StandbyIsOn; } + } + bool _StandbyIsOn; + + + /// + /// Dials, yo! + /// + public override void Dial(string number) + { + Debug.Console(1, this, "Dial: {0}", number); + var call = new CodecActiveCallItem() { Name = number, Number = number, Id = number, Status = eCodecCallStatus.Dialing, Direction = eCodecCallDirection.Outgoing, Type = eCodecCallType.Video }; + ActiveCalls.Add(call); + OnCallStatusChange(call); + //ActiveCallCountFeedback.FireUpdate(); + // Simulate 2-second ring, then connecting, then connected + new CTimer(o => + { + call.Type = eCodecCallType.Video; + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connecting, call); + new CTimer(oo => SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connected, call), 1000); + }, 2000); + } + + public override void Dial(Meeting meeting) + { + throw new NotImplementedException(); + } + + /// + /// + /// + public override void EndCall(CodecActiveCallItem call) + { + Debug.Console(1, this, "EndCall"); + ActiveCalls.Remove(call); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call); + //ActiveCallCountFeedback.FireUpdate(); + } + + /// + /// + /// + public override void EndAllCalls() + { + Debug.Console(1, this, "EndAllCalls"); + for(int i = ActiveCalls.Count - 1; i >= 0; i--) + { + var call = ActiveCalls[i]; + ActiveCalls.Remove(call); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call); + } + //ActiveCallCountFeedback.FireUpdate(); + } + + /// + /// For a call from the test methods below + /// + public override void AcceptCall(CodecActiveCallItem call) + { + Debug.Console(1, this, "AcceptCall"); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connecting, call); + new CTimer(o => SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connected, call), 1000); + // should already be in active list + } + + /// + /// For a call from the test methods below + /// + public override void RejectCall(CodecActiveCallItem call) + { + Debug.Console(1, this, "RejectCall"); + ActiveCalls.Remove(call); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Disconnected, call); + //ActiveCallCountFeedback.FireUpdate(); + } + + /// + /// Makes horrible tones go out on the wire! + /// + /// + public override void SendDtmf(string s) + { + Debug.Console(1, this, "SendDTMF: {0}", s); + } + + /// + /// + /// + public override void StartSharing() + { + _SharingIsOn = true; + SharingContentIsOnFeedback.FireUpdate(); + } + + /// + /// + /// + public override void StopSharing() + { + _SharingIsOn = false; + SharingContentIsOnFeedback.FireUpdate(); + } + + public override void StandbyActivate() + { + _StandbyIsOn = true; + } + + public override void StandbyDeactivate() + { + _StandbyIsOn = false; + } + + /// + /// Called by routing to make it happen + /// + /// + public override void ExecuteSwitch(object selector) + { + Debug.Console(1, this, "ExecuteSwitch: {0}", selector); + _SharingSource = selector.ToString(); + } + + /// + /// + /// + public override void MuteOff() + { + _IsMuted = false; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public override void MuteOn() + { + _IsMuted = true; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public override void MuteToggle() + { + _IsMuted = !_IsMuted; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + /// + public override void SetVolume(ushort level) + { + _VolumeLevel = level; + VolumeLevelFeedback.FireUpdate(); + } + + /// + /// + /// + /// + public override void VolumeDown(bool pressRelease) + { + } + + /// + /// + /// + /// + public override void VolumeUp(bool pressRelease) + { + } + + /// + /// + /// + public override void PrivacyModeOn() + { + Debug.Console(1, this, "PrivacyMuteOn"); + if (_PrivacyModeIsOn) + return; + _PrivacyModeIsOn = true; + PrivacyModeIsOnFeedback.FireUpdate(); + } + + /// + /// + /// + public override void PrivacyModeOff() + { + Debug.Console(1, this, "PrivacyMuteOff"); + if (!_PrivacyModeIsOn) + return; + _PrivacyModeIsOn = false; + PrivacyModeIsOnFeedback.FireUpdate(); + } + + /// + /// + /// + public override void PrivacyModeToggle() + { + _PrivacyModeIsOn = !_PrivacyModeIsOn; + Debug.Console(1, this, "PrivacyMuteToggle: {0}", _PrivacyModeIsOn); + PrivacyModeIsOnFeedback.FireUpdate(); + } + + //******************************************************** + // SIMULATION METHODS + + /// + /// + /// + /// + public void TestIncomingVideoCall(string url) + { + Debug.Console(1, this, "TestIncomingVideoCall from {0}", url); + var call = new CodecActiveCallItem() { Name = url, Id = url, Number = url, Type= eCodecCallType.Video, Direction = eCodecCallDirection.Incoming }; + ActiveCalls.Add(call); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Ringing, call); + + //OnCallStatusChange(eCodecCallStatus.Unknown, eCodecCallStatus.Ringing, call); + + } + + /// + /// + /// + /// + public void TestIncomingAudioCall(string url) + { + Debug.Console(1, this, "TestIncomingAudioCall from {0}", url); + var call = new CodecActiveCallItem() { Name = url, Id = url, Number = url, Type = eCodecCallType.Audio, Direction = eCodecCallDirection.Incoming }; + ActiveCalls.Add(call); + SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Ringing, call); + + //OnCallStatusChange(eCodecCallStatus.Unknown, eCodecCallStatus.Ringing, call); + } + + /// + /// + /// + public void TestFarEndHangup() + { + Debug.Console(1, this, "TestFarEndHangup"); + + } + + + #region IHasCallHistory Members + + public CodecCallHistory CallHistory { get; private set; } + + public void RemoveCallHistoryEntry(CodecCallHistory.CallHistoryEntry entry) + { + + } + + #endregion + + #region IHasScheduleAwareness Members + + public void GetSchedule() + { + + } + + public CodecScheduleAwareness CodecSchedule + { + get { + // if the last meeting has past, generate a new list + if (_CodecSchedule == null || _CodecSchedule.Meetings.Count == 0 + || _CodecSchedule.Meetings[_CodecSchedule.Meetings.Count - 1].StartTime < DateTime.Now) + { + _CodecSchedule = new CodecScheduleAwareness(); + for (int i = 0; i < 5; i++) + { + var m = new Meeting(); + m.StartTime = DateTime.Now.AddMinutes(3).AddHours(i); + m.EndTime = DateTime.Now.AddHours(i).AddMinutes(30); + m.Title = "Meeting " + i; + m.Calls.Add(new Call() { Number = i + "meeting@fake.com"}); + _CodecSchedule.Meetings.Add(m); + } + } + return _CodecSchedule; + } + } + CodecScheduleAwareness _CodecSchedule; + + #endregion + + #region IHasDirectory Members + + public event EventHandler DirectoryResultReturned; + + + public CodecDirectory DirectoryRoot + { + get + { + return MockVideoCodecDirectory.DirectoryRoot; + } + } + + public CodecDirectory CurrentDirectoryResult + { + get + { + if (DirectoryBrowseHistory.Count > 0) + return DirectoryBrowseHistory[DirectoryBrowseHistory.Count - 1]; + else + return DirectoryRoot; + } + } + + public CodecPhonebookSyncState PhonebookSyncState + { + get + { + var syncState = new CodecPhonebookSyncState(Key + "PhonebookSync"); + + syncState.InitialPhonebookFoldersReceived(); + syncState.PhonebookRootEntriesReceived(); + syncState.SetPhonebookHasFolders(true); + syncState.SetNumberOfContacts(0); // just need to call this method for the sync to complete + + return syncState; + } + } + + public void SearchDirectory(string searchString) + { + var searchResults = new CodecDirectory(); + + searchResults.ResultsFolderId = "searchResult"; + + // Search mock directory for contacts that contain the search string, ignoring case + List matches = MockVideoCodecDirectory.CompleteDirectory.CurrentDirectoryResults.FindAll( + s => s is DirectoryContact && s.Name.ToLower().Contains(searchString.ToLower())); + + if (matches != null) + { + searchResults.AddContactsToDirectory(matches); + + DirectoryBrowseHistory.Add(searchResults); + } + + OnDirectoryResultReturned(searchResults); + } + + public void GetDirectoryFolderContents(string folderId) + { + var folderDirectory = new CodecDirectory(); + + if (folderId == MockVideoCodecDirectory.eFolderId.UnitedStates.ToString()) + folderDirectory = MockVideoCodecDirectory.UnitedStatesFolderContents; + else if (folderId == MockVideoCodecDirectory.eFolderId.Canada.ToString()) + folderDirectory = MockVideoCodecDirectory.CanadaFolderContents; + else if (folderId == MockVideoCodecDirectory.eFolderId.NewYork.ToString()) + folderDirectory = MockVideoCodecDirectory.NewYorkFolderContents; + else if (folderId == MockVideoCodecDirectory.eFolderId.Boston.ToString()) + folderDirectory = MockVideoCodecDirectory.BostonFolderContents; + else if (folderId == MockVideoCodecDirectory.eFolderId.SanFrancisco.ToString()) + folderDirectory = MockVideoCodecDirectory.SanFranciscoFolderContents; + else if (folderId == MockVideoCodecDirectory.eFolderId.Denver.ToString()) + folderDirectory = MockVideoCodecDirectory.DenverFolderContents; + else if (folderId == MockVideoCodecDirectory.eFolderId.Austin.ToString()) + folderDirectory = MockVideoCodecDirectory.AustinFolderContents; + else if (folderId == MockVideoCodecDirectory.eFolderId.Calgary.ToString()) + folderDirectory = MockVideoCodecDirectory.CalgaryFolderContents; + + DirectoryBrowseHistory.Add(folderDirectory); + + OnDirectoryResultReturned(folderDirectory); + } + + public void SetCurrentDirectoryToRoot() + { + DirectoryBrowseHistory.Clear(); + + OnDirectoryResultReturned(DirectoryRoot); + } + + public void GetDirectoryParentFolderContents() + { + var currentDirectory = new CodecDirectory(); + + if (DirectoryBrowseHistory.Count > 0) + { + var lastItemIndex = DirectoryBrowseHistory.Count - 1; + var parentDirectoryContents = DirectoryBrowseHistory[lastItemIndex]; + + DirectoryBrowseHistory.Remove(DirectoryBrowseHistory[lastItemIndex]); + + currentDirectory = parentDirectoryContents; + + } + else + { + currentDirectory = DirectoryRoot; + } + + OnDirectoryResultReturned(currentDirectory); + } + + public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } + + public List DirectoryBrowseHistory { get; private set; } + + public void OnDirectoryResultReturned(CodecDirectory result) + { + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + + var handler = DirectoryResultReturned; + if (handler != null) + { + handler(this, new DirectoryEventArgs() + { + Directory = result, + DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue + }); + } + } + + #endregion + + void SetupCameras() + { + Cameras = new List(); + + var internalCamera = new MockVCCamera(Key + "-camera1", "Near End", this); + + Cameras.Add(internalCamera); + + var farEndCamera = new MockFarEndVCCamera(Key + "-cameraFar", "Far End", this); + + Cameras.Add(farEndCamera); + + SelectedCameraFeedback = new StringFeedback(() => SelectedCamera.Key); + + ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera); + + CameraAutoModeIsOnFeedback = new BoolFeedback(() => _CameraAutoModeIsOn); + + CameraAutoModeIsOnFeedback.FireUpdate(); + + DeviceManager.AddDevice(internalCamera); + DeviceManager.AddDevice(farEndCamera); + + NearEndPresets = new List(15); // Fix the capacity to emulate Cisco + + NearEndPresets = PropertiesConfig.Presets; + + FarEndRoomPresets = new List(15); // Fix the capacity to emulate Cisco + + // Add the far end presets + for (int i = 1; i <= FarEndRoomPresets.Capacity; i++) + { + var label = string.Format("Far End Preset {0}", i); + FarEndRoomPresets.Add(new CodecRoomPreset(i, label, true, false)); + } + + SelectedCamera = internalCamera; ; // call the method to select the camera and ensure the feedbacks get updated. + } + + #region IHasCameras Members + + public event EventHandler CameraSelected; + + public List Cameras { get; private set; } + + private CameraBase _selectedCamera; + + /// + /// Returns the selected camera + /// + public CameraBase SelectedCamera + { + get + { + return _selectedCamera; + } + private set + { + _selectedCamera = value; + SelectedCameraFeedback.FireUpdate(); + ControllingFarEndCameraFeedback.FireUpdate(); + + var handler = CameraSelected; + if (handler != null) + { + handler(this, new CameraSelectedEventArgs(SelectedCamera)); + } + } + } + + public StringFeedback SelectedCameraFeedback { get; private set; } + + public void SelectCamera(string key) + { + var camera = Cameras.FirstOrDefault(c => c.Key.ToLower().IndexOf(key.ToLower()) > -1); + if (camera != null) + { + Debug.Console(2, this, "Selected Camera with key: '{0}'", camera.Key); + SelectedCamera = camera; + } + else + Debug.Console(2, this, "Unable to select camera with key: '{0}'", key); + } + + #endregion + + #region IHasFarEndCameraControl Members + + public CameraBase FarEndCamera { get; private set; } + + public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } + + #endregion + + #region IHasCameraAutoMode Members + + private bool _CameraAutoModeIsOn; + + public void CameraAutoModeOn() + { + _CameraAutoModeIsOn = true; + CameraAutoModeIsOnFeedback.FireUpdate(); + } + + public void CameraAutoModeOff() + { + _CameraAutoModeIsOn = false; + CameraAutoModeIsOnFeedback.FireUpdate(); + } + + public void CameraAutoModeToggle() + { + if(_CameraAutoModeIsOn) + _CameraAutoModeIsOn = false; + else + _CameraAutoModeIsOn = true; + + CameraAutoModeIsOnFeedback.FireUpdate(); + + } + + public BoolFeedback CameraAutoModeIsOnFeedback {get; private set;} + + #endregion + + #region IHasCameraPresets Members + + public event EventHandler CodecRoomPresetsListHasChanged; + + public List NearEndPresets { get; private set; } + + public List FarEndRoomPresets { get; private set; } + + public void CodecRoomPresetSelect(int preset) + { + if (SelectedCamera is IAmFarEndCamera) + { + Debug.Console(1, this, "Selecting Far End Preset: {0}", preset); + } + else + { + Debug.Console(1, this, "Selecting Near End Preset: {0}", preset); + } + } + + public void CodecRoomPresetStore(int preset, string description) + { + var editPreset = NearEndPresets.FirstOrDefault(p => p.ID.Equals(preset)); + + if (editPreset != null) + { + editPreset.Defined = true; + editPreset.Description = description; + } + else + NearEndPresets.Add(new CodecRoomPreset(preset, description, true, true)); + + var handler = CodecRoomPresetsListHasChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + + // Update the config + SetConfig(Config); + } + + #endregion + + protected override void CustomSetConfig(DeviceConfig config) + { + PropertiesConfig.Presets = NearEndPresets; + + Config.Properties = JToken.FromObject(PropertiesConfig); + + ConfigWriter.UpdateDeviceConfig(config); + } + + } + + /// + /// Implementation for the mock VC + /// + public class MockCodecInfo : VideoCodecInfo + { + + public override bool MultiSiteOptionIsEnabled + { + get { return true; } + } + + public override string E164Alias + { + get { return "someE164alias"; } + } + + public override string H323Id + { + get { return "someH323Id"; } + } + + public override string IpAddress + { + get { return "xxx.xxx.xxx.xxx"; } + } + + public override string SipPhoneNumber + { + get { return "333-444-5555"; } + } + + public override string SipUri + { + get { return "mock@someurl.com"; } + } + + public override bool AutoAnswerEnabled + { + get { return _AutoAnswerEnabled; } + } + bool _AutoAnswerEnabled; + + public void SetAutoAnswer(bool value) + { + _AutoAnswerEnabled = value; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs.orig b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs.orig new file mode 100644 index 00000000..f70988dd --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs.orig @@ -0,0 +1,250 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public class MockVC : VideoCodecBase + { + public MockVC(string key, string name) + : base(key, name) + { + // Debug helpers + ActiveCallCountFeedback.OutputChange += (o, a) => Debug.Console(1, this, "InCall={0}", ActiveCallCountFeedback.IntValue); + IncomingCallFeedback.OutputChange += (o, a) => Debug.Console(1, this, "IncomingCall={0}", _IncomingCall); + MuteFeedback.OutputChange += (o, a) => Debug.Console(1, this, "Mute={0}", _IsMuted); + PrivacyModeIsOnFeedback.OutputChange += (o, a) => Debug.Console(1, this, "Privacy={0}", _PrivacyModeIsOn); + SharingSourceFeedback.OutputChange += (o, a) => Debug.Console(1, this, "SharingSource={0}", _SharingSource); + VolumeLevelFeedback.OutputChange += (o, a) => Debug.Console(1, this, "Volume={0}", _VolumeLevel); + } + + protected override Func ActiveCallCountFeedbackFunc + { + get { return () => ActiveCalls.Count; } + } + + protected override Func IncomingCallFeedbackFunc + { + get { return () => _IncomingCall; } + } + bool _IncomingCall; + + protected override Func MuteFeedbackFunc + { + get { return () => _IsMuted; } + } + bool _IsMuted; + + protected override Func PrivacyModeIsOnFeedbackFunc + { + get { return () => _PrivacyModeIsOn; } + } + bool _PrivacyModeIsOn; + + protected override Func SharingSourceFeedbackFunc + { + get { return () => _SharingSource; } + } + string _SharingSource; + + protected override Func VolumeLevelFeedbackFunc + { + get { return () => _VolumeLevel; } + } + int _VolumeLevel; + + /// + /// Dials, yo! + /// + public override void Dial(string s) + { + Debug.Console(1, this, "Dial: {0}", s); + ActiveCalls.Add(new CodecActiveCallItem(s,s, eCodecCallType.Video)); + ActiveCallCountFeedback.FireUpdate(); + } + + /// + /// + /// + public override void EndCall(CodecActiveCallItem activeCall) + { + Debug.Console(1, this, "EndCall"); + ActiveCalls.RemoveAll(i => i.Name == s); + ActiveCallCountFeedback.FireUpdate(); + //_InCall = false; + //IsInCall.FireUpdate(); + } + + public override void EndAllCalls() + { + + } + + /// + /// For a call from the test methods below + /// + public override void AcceptCall() + { + Debug.Console(1, this, "AcceptCall"); + } + + /// + /// For a call from the test methods below + /// + public override void RejectCall() + { + Debug.Console(1, this, "RejectCall"); + } + + /// + /// Makes horrible tones go out on the wire! + /// + /// + public override void SendDtmf(string s) + { + Debug.Console(1, this, "SendDTMF: {0}", s); + } + + /// + /// + /// + public override void StartSharing() + { + } + + /// + /// + /// + public override void StopSharing() + { + } + + /// + /// Called by routing to make it happen + /// + /// + public override void ExecuteSwitch(object selector) + { + Debug.Console(1, this, "ExecuteSwitch"); + _SharingSource = selector.ToString(); + + } + + /// + /// + /// + public override void MuteOff() + { + _IsMuted = false; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public override void MuteOn() + { + _IsMuted = true; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + public override void MuteToggle() + { + _IsMuted = !_IsMuted; + MuteFeedback.FireUpdate(); + } + + /// + /// + /// + /// + public override void SetVolume(ushort level) + { + _VolumeLevel = level; + VolumeLevelFeedback.FireUpdate(); + } + + /// + /// + /// + /// + public override void VolumeDown(bool pressRelease) + { + } + + /// + /// + /// + /// + public override void VolumeUp(bool pressRelease) + { + } + + /// + /// + /// + public override void PrivacyModeOn() + { + Debug.Console(1, this, "PrivacyMuteOn"); + if (_PrivacyModeIsOn) + return; + _PrivacyModeIsOn = true; + PrivacyModeIsOnFeedback.FireUpdate(); + + } + + /// + /// + /// + public override void PrivacyModeOff() + { + Debug.Console(1, this, "PrivacyMuteOff"); + if (!_PrivacyModeIsOn) + return; + _PrivacyModeIsOn = false; + PrivacyModeIsOnFeedback.FireUpdate(); + } + + /// + /// + /// + public override void PrivacyModeToggle() + { + _PrivacyModeIsOn = !_PrivacyModeIsOn; + Debug.Console(1, this, "PrivacyMuteToggle: {0}", _PrivacyModeIsOn); + PrivacyModeIsOnFeedback.FireUpdate(); + } + + //******************************************************** + // SIMULATION METHODS + + /// + /// + /// + /// + public void TestIncomingCall(string url) + { + Debug.Console(1, this, "TestIncomingCall"); + + _IncomingCall = true; + IncomingCallFeedback.FireUpdate(); + } + + /// + /// + /// + public void TestFarEndHangup() + { + Debug.Console(1, this, "TestFarEndHangup"); + + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVCCamera.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVCCamera.cs new file mode 100644 index 00000000..c404ac89 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVCCamera.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.Devices.Common.Cameras +{ + public class MockVCCamera : CameraBase, IHasCameraPtzControl, IHasCameraFocusControl + { + protected VideoCodecBase ParentCodec { get; private set; } + + + public MockVCCamera(string key, string name, VideoCodecBase codec) + : base(key, name) + { + Capabilities = eCameraCapabilities.Pan | eCameraCapabilities.Tilt | eCameraCapabilities.Zoom | eCameraCapabilities.Focus; + + ParentCodec = codec; + } + + #region IHasCameraPtzControl Members + + public void PositionHome() + { + Debug.Console(1, this, "Resetting to home position"); + } + + #endregion + + #region IHasCameraPanControl Members + + public void PanLeft() + { + Debug.Console(1, this, "Panning Left"); + } + + public void PanRight() + { + Debug.Console(1, this, "Panning Right"); + } + + public void PanStop() + { + Debug.Console(1, this, "Stopping Pan"); + } + + #endregion + + #region IHasCameraTiltControl Members + + public void TiltDown() + { + Debug.Console(1, this, "Tilting Down"); + } + + public void TiltUp() + { + Debug.Console(1, this, "Tilting Up"); + } + + public void TiltStop() + { + Debug.Console(1, this, "Stopping Tilt"); + } + + #endregion + + #region IHasCameraZoomControl Members + + public void ZoomIn() + { + Debug.Console(1, this, "Zooming In"); + } + + public void ZoomOut() + { + Debug.Console(1, this, "Zooming Out"); + } + + public void ZoomStop() + { + Debug.Console(1, this, "Stopping Zoom"); + } + + #endregion + + #region IHasCameraFocusControl Members + + public void FocusNear() + { + Debug.Console(1, this, "Focusing Near"); + } + + public void FocusFar() + { + Debug.Console(1, this, "Focusing Far"); + } + + public void FocusStop() + { + Debug.Console(1, this, "Stopping Focus"); + } + + public void TriggerAutoFocus() + { + Debug.Console(1, this, "AutoFocus Triggered"); + } + + #endregion + } + + public class MockFarEndVCCamera : CameraBase, IHasCameraPtzControl, IAmFarEndCamera + { + protected VideoCodecBase ParentCodec { get; private set; } + + + public MockFarEndVCCamera(string key, string name, VideoCodecBase codec) + : base(key, name) + { + Capabilities = eCameraCapabilities.Pan | eCameraCapabilities.Tilt | eCameraCapabilities.Zoom; + + ParentCodec = codec; + } + + #region IHasCameraPtzControl Members + + public void PositionHome() + { + Debug.Console(1, this, "Resetting to home position"); + } + + #endregion + + #region IHasCameraPanControl Members + + public void PanLeft() + { + Debug.Console(1, this, "Panning Left"); + } + + public void PanRight() + { + Debug.Console(1, this, "Panning Right"); + } + + public void PanStop() + { + Debug.Console(1, this, "Stopping Pan"); + } + + #endregion + + #region IHasCameraTiltControl Members + + public void TiltDown() + { + Debug.Console(1, this, "Tilting Down"); + } + + public void TiltUp() + { + Debug.Console(1, this, "Tilting Up"); + } + + public void TiltStop() + { + Debug.Console(1, this, "Stopping Tilt"); + } + + #endregion + + #region IHasCameraZoomControl Members + + public void ZoomIn() + { + Debug.Console(1, this, "Zooming In"); + } + + public void ZoomOut() + { + Debug.Console(1, this, "Zooming Out"); + } + + public void ZoomStop() + { + Debug.Console(1, this, "Stopping Zoom"); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVcPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVcPropertiesConfig.cs new file mode 100644 index 00000000..6a790af6 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVcPropertiesConfig.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public class MockVcPropertiesConfig + { + [JsonProperty("favorites")] + public List Favorites { get; set; } + + [JsonProperty("presets")] + public List Presets { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs new file mode 100644 index 00000000..1f8938f6 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs @@ -0,0 +1,328 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Devices; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common; +using PepperDash.Essentials.Devices.Common.Codec; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, + IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo + { + /// + /// Fires when the status of any active, dialing, or incoming call changes or is new + /// + public event EventHandler CallStatusChange; + + public event EventHandler IsReadyChange; + + public IBasicCommunication Communication { get; protected set; } + + #region IUsageTracking Members + + /// + /// This object can be added by outside users of this class to provide usage tracking + /// for various services + /// + public UsageTracking UsageTracker { get; set; } + + #endregion + + /// + /// An internal pseudo-source that is routable and connected to the osd input + /// + public DummyRoutingInputsDevice OsdSource { get; protected set; } + + public RoutingPortCollection InputPorts { get; private set; } + + public RoutingPortCollection OutputPorts { get; private set; } + + /// + /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected + /// + public bool IsInCall + { + get + { + bool value; + + if (ActiveCalls != null) + value = ActiveCalls.Any(c => c.IsActiveCall); + else + value = false; + return value; + } + } + + public BoolFeedback StandbyIsOnFeedback { get; private set; } + + abstract protected Func PrivacyModeIsOnFeedbackFunc { get; } + abstract protected Func VolumeLevelFeedbackFunc { get; } + abstract protected Func MuteFeedbackFunc { get; } + abstract protected Func StandbyIsOnFeedbackFunc { get; } + + public List ActiveCalls { get; set; } + + public VideoCodecInfo CodecInfo { get; protected set; } + + public bool ShowSelfViewByDefault { get; protected set; } + + + public bool IsReady { get; protected set; } + + public VideoCodecBase(DeviceConfig config) + : base(config) + { + StandbyIsOnFeedback = new BoolFeedback(StandbyIsOnFeedbackFunc); + PrivacyModeIsOnFeedback = new BoolFeedback(PrivacyModeIsOnFeedbackFunc); + VolumeLevelFeedback = new IntFeedback(VolumeLevelFeedbackFunc); + MuteFeedback = new BoolFeedback(MuteFeedbackFunc); + SharingSourceFeedback = new StringFeedback(SharingSourceFeedbackFunc); + SharingContentIsOnFeedback = new BoolFeedback(SharingContentIsOnFeedbackFunc); + + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); + + ActiveCalls = new List(); + } + + #region IHasDialer Members + + public abstract void Dial(string number); + public abstract void Dial(Meeting meeting); + public virtual void Dial(IInvitableContact contact) + { + + } + public abstract void EndCall(CodecActiveCallItem call); + public abstract void EndAllCalls(); + public abstract void AcceptCall(CodecActiveCallItem call); + public abstract void RejectCall(CodecActiveCallItem call); + public abstract void SendDtmf(string s); + + #endregion + + public virtual List Feedbacks + { + get + { + return new List + { + PrivacyModeIsOnFeedback, + SharingSourceFeedback + }; + } + } + + public abstract void ExecuteSwitch(object selector); + + /// + /// Helper method to fire CallStatusChange event with old and new status + /// + protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call) + { + call.Status = newStatus; + + OnCallStatusChange(call); + + } + + /// + /// + /// + /// + /// + /// + protected void OnCallStatusChange(CodecActiveCallItem item) + { + var handler = CallStatusChange; + if (handler != null) + handler(this, new CodecCallStatusItemChangeEventArgs(item)); + + if (AutoShareContentWhileInCall) + StartSharing(); + + if (UsageTracker != null) + { + if (IsInCall && !UsageTracker.UsageTrackingStarted) + UsageTracker.StartDeviceUsage(); + else if (UsageTracker.UsageTrackingStarted && !IsInCall) + UsageTracker.EndDeviceUsage(); + } + } + + /// + /// Sets IsReady property and fires the event. Used for dependent classes to sync up their data. + /// + protected void SetIsReady() + { + IsReady = true; + var h = IsReadyChange; + if(h != null) + h(this, new EventArgs()); + } + + #region ICodecAudio Members + + public abstract void PrivacyModeOn(); + public abstract void PrivacyModeOff(); + public abstract void PrivacyModeToggle(); + public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } + + + public BoolFeedback MuteFeedback { get; private set; } + + public abstract void MuteOff(); + + public abstract void MuteOn(); + + public abstract void SetVolume(ushort level); + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public abstract void MuteToggle(); + + public abstract void VolumeDown(bool pressRelease); + + + public abstract void VolumeUp(bool pressRelease); + + #endregion + + #region IHasSharing Members + + public abstract void StartSharing(); + public abstract void StopSharing(); + + public bool AutoShareContentWhileInCall { get; protected set; } + + public StringFeedback SharingSourceFeedback { get; private set; } + public BoolFeedback SharingContentIsOnFeedback { get; private set; } + + abstract protected Func SharingSourceFeedbackFunc { get; } + abstract protected Func SharingContentIsOnFeedbackFunc { get; } + + + #endregion + + // **** DEBUGGING THINGS **** + /// + /// + /// + public virtual void ListCalls() + { + var sb = new StringBuilder(); + foreach (var c in ActiveCalls) + sb.AppendFormat("{0} {1} -- {2} {3}\n", c.Id, c.Number, c.Name, c.Status); + Debug.Console(1, this, "\n{0}\n", sb.ToString()); + } + + public abstract void StandbyActivate(); + + public abstract void StandbyDeactivate(); + + } + + + /// + /// Used to track the status of syncronizing the phonebook values when connecting to a codec or refreshing the phonebook info + /// + public class CodecPhonebookSyncState : IKeyed + { + bool _InitialSyncComplete; + + public event EventHandler InitialSyncCompleted; + + public string Key { get; private set; } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + handler(this, new EventArgs()); + } + _InitialSyncComplete = value; + } + } + + public bool InitialPhonebookFoldersWasReceived { get; private set; } + + public bool NumberOfContactsWasReceived { get; private set; } + + public bool PhonebookRootEntriesWasRecieved { get; private set; } + + public bool PhonebookHasFolders { get; private set; } + + public int NumberOfContacts { get; private set; } + + public CodecPhonebookSyncState(string key) + { + Key = key; + + CodecDisconnected(); + } + + public void InitialPhonebookFoldersReceived() + { + InitialPhonebookFoldersWasReceived = true; + + CheckSyncStatus(); + } + + public void PhonebookRootEntriesReceived() + { + PhonebookRootEntriesWasRecieved = true; + + CheckSyncStatus(); + } + + public void SetPhonebookHasFolders(bool value) + { + PhonebookHasFolders = value; + + Debug.Console(1, this, "Phonebook has folders: {0}", PhonebookHasFolders); + } + + public void SetNumberOfContacts(int contacts) + { + NumberOfContacts = contacts; + NumberOfContactsWasReceived = true; + + Debug.Console(1, this, "Phonebook contains {0} contacts.", NumberOfContacts); + + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + InitialPhonebookFoldersWasReceived = false; + PhonebookHasFolders = false; + NumberOfContacts = 0; + NumberOfContactsWasReceived = false; + } + + void CheckSyncStatus() + { + if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Phonebook Sync Complete!"); + } + else + InitialSyncComplete = false; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs.orig b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs.orig new file mode 100644 index 00000000..e13bb133 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs.orig @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public abstract class VideoCodecBase : Device, IRoutingSinkWithSwitching, IUsageTracking, IHasDialer, IHasSharing, ICodecAudio + { + #region IUsageTracking Members + + /// + /// This object can be added by outside users of this class to provide usage tracking + /// for various services + /// + public UsageTracking UsageTracker { get; set; } + + #endregion + + #region IRoutingInputs Members + + public RoutingPortCollection InputPorts { get; private set; } + + #endregion + + /// + /// Returns true when ActiveCallCountFeedback is > 0 + /// + public bool IsInCall { get { return ActiveCallCountFeedback.IntValue > 0; } } + + public BoolFeedback IncomingCallFeedback { get; private set; } + + public IntFeedback ActiveCallCountFeedback { get; private set; } + + abstract protected Func ActiveCallCountFeedbackFunc { get; } + abstract protected Func IncomingCallFeedbackFunc { get; } + abstract protected Func PrivacyModeIsOnFeedbackFunc { get; } + abstract protected Func VolumeLevelFeedbackFunc { get; } + abstract protected Func MuteFeedbackFunc { get; } + abstract protected Func SharingSourceFeedbackFunc { get; } + + public List ActiveCalls { get; set; } + + public VideoCodecBase(string key, string name) + : base(key, name) + { + ActiveCallCountFeedback = new IntFeedback(ActiveCallCountFeedbackFunc); + IncomingCallFeedback = new BoolFeedback(IncomingCallFeedbackFunc); + PrivacyModeIsOnFeedback = new BoolFeedback(PrivacyModeIsOnFeedbackFunc); + VolumeLevelFeedback = new IntFeedback(VolumeLevelFeedbackFunc); + MuteFeedback = new BoolFeedback(MuteFeedbackFunc); + SharingSourceFeedback = new StringFeedback(SharingSourceFeedbackFunc); + + InputPorts = new RoutingPortCollection(); + + ActiveCallCountFeedback.OutputChange += new EventHandler(ActiveCallCountFeedback_OutputChange); + + ActiveCalls = new List(); + } + + /// + /// + /// + /// + /// + void ActiveCallCountFeedback_OutputChange(object sender, EventArgs e) + { + if (UsageTracker != null) + { + if (IsInCall) + UsageTracker.StartDeviceUsage(); + else + UsageTracker.EndDeviceUsage(); + } + } + #region IHasDialer Members + + public abstract void Dial(string s); +<<<<<<< HEAD + public abstract void EndCall(string s); +======= + public void EndCall(object activeCall) + { + + } + public abstract void EndCall(CodecActiveCallItem activeCall); + public abstract void EndAllCalls(); +>>>>>>> origin/feature/cisco-spark-2 + public abstract void AcceptCall(); + public abstract void RejectCall(); + public abstract void SendDtmf(string s); + + #endregion + + public virtual List Feedbacks + { + get + { + return new List + { + IncomingCallFeedback, + PrivacyModeIsOnFeedback, + SharingSourceFeedback + }; + } + } + + public abstract void ExecuteSwitch(object selector); + + #region ICodecAudio Members + + public abstract void PrivacyModeOn(); + public abstract void PrivacyModeOff(); + public abstract void PrivacyModeToggle(); + public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } + + + public BoolFeedback MuteFeedback { get; private set; } + + public abstract void MuteOff(); + + public abstract void MuteOn(); + + public abstract void SetVolume(ushort level); + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public abstract void MuteToggle(); + + public abstract void VolumeDown(bool pressRelease); + + + public abstract void VolumeUp(bool pressRelease); + + #endregion + + #region IHasSharing Members + + public abstract void StartSharing(); + public abstract void StopSharing(); + + public StringFeedback SharingSourceFeedback { get; private set; } + + #endregion + + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs new file mode 100644 index 00000000..3a78c5e0 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs @@ -0,0 +1,1184 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Devices.Common.Codec; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom +{ + public enum eZoomRoomResponseType + { + zEvent, + zStatus, + zConfiguration, + zCommand + } + + public abstract class NotifiableObject : INotifyPropertyChanged + { + #region INotifyPropertyChanged Members + + public event PropertyChangedEventHandler PropertyChanged; + + protected void NotifyPropertyChanged(string propertyName) + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + + #endregion + } + + /// + /// Used to track the current status of a ZoomRoom + /// + public class ZoomRoomStatus + { + public zStatus.Login Login { get; set; } + public zStatus.SystemUnit SystemUnit { get; set; } + public zStatus.Phonebook Phonebook { get; set; } + public zStatus.Call Call { get; set; } + public zStatus.Capabilities Capabilities { get; set; } + public zStatus.Sharing Sharing { get; set; } + public zStatus.NumberOfScreens NumberOfScreens { get; set; } + public zStatus.Layout Layout { get; set; } + public zStatus.Video Video { get; set; } + public zStatus.CameraShare CameraShare { get; set; } + public List AudioInputs { get; set; } + public List AudioOuputs { get; set; } + public List Cameras { get; set; } + + public ZoomRoomStatus() + { + Login = new zStatus.Login(); + SystemUnit = new zStatus.SystemUnit(); + Phonebook = new zStatus.Phonebook(); + Call = new zStatus.Call(); + Capabilities = new zStatus.Capabilities(); + Sharing = new zStatus.Sharing(); + NumberOfScreens = new zStatus.NumberOfScreens(); + Layout = new zStatus.Layout(); + Video = new zStatus.Video(); + CameraShare = new zStatus.CameraShare(); + AudioInputs = new List(); + AudioOuputs = new List(); + Cameras = new List(); + } + } + + /// + /// Used to track the current configuration of a ZoomRoom + /// + public class ZoomRoomConfiguration + { + public zConfiguration.Call Call { get; set; } + public zConfiguration.Audio Audio { get; set; } + public zConfiguration.Video Video { get; set; } + public zConfiguration.Client Client { get; set; } + + public ZoomRoomConfiguration() + { + Call = new zConfiguration.Call(); + Audio = new zConfiguration.Audio(); + Video = new zConfiguration.Video(); + Client = new zConfiguration.Client(); + } + } + + /// + /// Represents a response from a ZoomRoom system + /// + public class Response + { + public Status Status { get; set; } + public bool Sync { get; set; } + [JsonProperty("topKey")] + public string TopKey { get; set; } + [JsonProperty("type")] + public string Type { get; set; } + + public Response() + { + Status = new Status(); + } + } + + public class Status + { + [JsonProperty("message")] + public string Message { get; set; } + [JsonProperty("state")] + public string State { get; set; } + } + + + /// + /// zStatus class stucture + /// + public class zStatus + { + public class Login + { + [JsonProperty("ZAAPI Release")] + public string ZAAPIRelease { get; set; } + [JsonProperty("Zoom Room Release")] + public string ZoomRoomRelease { get; set; } + } + + public class SystemUnit + { + [JsonProperty("email")] + public string Email { get; set; } + [JsonProperty("login_type")] + public string LoginType { get; set; } + [JsonProperty("meeting_number")] + public string MeetingNumber { get; set; } + [JsonProperty("platform")] + public string Platform { get; set; } + [JsonProperty("room_info")] + public RoomInfo RoomInfo { get; set; } + [JsonProperty("room_version")] + public string RoomVersion { get; set; } + + public SystemUnit() + { + RoomInfo = new RoomInfo(); + } + } + + public class RoomInfo + { + [JsonProperty("account_email")] + public string AccountEmail { get; set; } + [JsonProperty("display_version")] + public string DisplayVersion { get; set; } + [JsonProperty("is_auto_answer_enabled")] + public bool AutoAnswerIsEnabled { get; set; } + [JsonProperty("is_auto_answer_selected")] + public bool AutoAnswerIsSelected { get; set; } + [JsonProperty("room_name")] + public string RoomName { get; set; } + } + + public class CloudPbxInfo + { + [JsonProperty("company_number")] + public string CompanyNumber { get; set; } + [JsonProperty("extension")] + public string Extension { get; set; } + [JsonProperty("isValid")] + public bool IsValid { get; set; } + } + + public enum ePresence + { + PRESENCE_OFFLINE, + PRESENCE_ONLINE, + PRESENCE_AWAY, + PRESENCE_BUSY, + PRESENCE_DND + } + + public class Contact + { + [JsonProperty("avatarURL")] + public string AvatarURL { get; set; } + [JsonProperty("cloud_pbx_info")] + public CloudPbxInfo CloudPbxInfo { get; set; } + [JsonProperty("email")] + public string Email { get; set; } + [JsonProperty("firstName")] + public string FirstName { get; set; } + [JsonProperty("index")] + public int Index { get; set; } + [JsonProperty("isLegacy")] + public bool IsLegacy { get; set; } + [JsonProperty("isZoomRoom")] + public bool IsZoomRoom { get; set; } + [JsonProperty("jid")] + public string Jid { get; set; } + [JsonProperty("lastName")] + public string LastName { get; set; } + [JsonProperty("onDesktop")] + public bool OnDesktop { get; set; } + [JsonProperty("onMobile")] + public bool OnMobile { get; set; } + [JsonProperty("phoneNumber")] + public string PhoneNumber { get; set; } + [JsonProperty("presence")] + public ePresence Presence { get; set; } + [JsonProperty("presence_status")] + public int PresenceStatus { get; set; } + [JsonProperty("screenName")] + public string ScreenName { get; set; } + [JsonProperty("sip_phone_number")] + public string SipPhoneNumber { get; set; } + + + public Contact() + { + CloudPbxInfo = new CloudPbxInfo(); + } + } + + /// + /// Used to be able to inplement IInvitableContact on DirectoryContact + /// + public class ZoomDirectoryContact : DirectoryContact, IInvitableContact + { + + } + + public class Phonebook + { + [JsonProperty("Contacts")] + public List Contacts { get; set; } + + public Phonebook() + { + Contacts = new List(); + } + + /// + /// Converts from zStatus.Contact types to generic directory items + /// + /// + public static CodecDirectory ConvertZoomContactsToGeneric(List zoomContacts) + { + var directory = new Codec.CodecDirectory(); + + var folders = new List(); + + var roomFolder = new DirectoryFolder(); + + var contactFolder = new DirectoryFolder(); + + var contacts = new List(); + + // Check if there are any zoom rooms + var zoomRooms = zoomContacts.FindAll(c => c.IsZoomRoom); + + if (zoomRooms.Count > 0) + { + // If so, setup a rooms and contacts folder and add them. + roomFolder.Name = "Rooms"; + roomFolder.FolderId = "rooms"; + + contactFolder.Name = "Contacts"; + contactFolder.FolderId = "contacts"; + + folders.Add(roomFolder); + folders.Add(contactFolder); + + directory.AddFoldersToDirectory(folders); + } + + try + { + if (zoomContacts.Count > 0) + { + foreach (Contact c in zoomContacts) + { + var contact = new ZoomDirectoryContact(); + + contact.Name = c.ScreenName; + contact.ContactId = c.Jid; + + if (folders.Count > 0) + { + if (c.IsZoomRoom) + contact.FolderId = roomFolder.FolderId; + else + contact.FolderId = contactFolder.FolderId; + } + + contacts.Add(contact); + } + + directory.AddContactsToDirectory(contacts); + } + } + catch (Exception e) + { + Debug.Console(1, "Error converting Zoom Phonebook results to generic: {0}", e); + } + + return directory; + } + } + + public enum eCallStatus + { + UNKNOWN, + NOT_IN_MEETING, + CONNECTING_MEETING, + IN_MEETING, + LOGGED_OUT + } + + public class ClosedCaption + { + public bool Available { get; set; } + } + + public class Call : NotifiableObject + { + private eCallStatus _status; + private List _participants; + + public bool IsInCall; + + public eCallStatus Status + { + get + { + return _status; + } + set + { + if (value != _status) + { + _status = value; + IsInCall = _status == eCallStatus.IN_MEETING || _status == eCallStatus.CONNECTING_MEETING; + NotifyPropertyChanged("Status"); + } + } + } + public ClosedCaption ClosedCaption { get; set; } + public List Participants + { + get + { + return _participants; + } + set + { + _participants = value; + NotifyPropertyChanged("Participants"); + } + } + public zEvent.SharingState Sharing { get; set; } + + public CallRecordInfo CallRecordInfo { get; set; } + + public zCommand.InfoResult Info { get; set; } + + public Call() + { + ClosedCaption = new ClosedCaption(); + Participants = new List(); + Sharing = new zEvent.SharingState(); + CallRecordInfo = new CallRecordInfo(); + Info = new zCommand.InfoResult(); + } + } + + public class Capabilities + { + public bool aec_Setting_Stored_In_ZR { get; set; } + public bool can_Dtmf_For_Invite_By_Phone { get; set; } + public bool can_Mute_On_Entry { get; set; } + public bool can_Ringing_In_Pstn_Call { get; set; } + public bool can_Switch_To_Specific_Camera { get; set; } + public bool is_Airhost_Disabled { get; set; } + public bool pstn_Call_In_Local_resentation { get; set; } + public bool support_Claim_Host { get; set; } + public bool support_Out_Room_Display { get; set; } + public bool support_Pin_And_Spotlight { get; set; } + public bool supports_Audio_Checkup { get; set; } + public bool supports_CheckIn { get; set; } + public bool supports_Cloud_PBX { get; set; } + public bool supports_Encrypted_Connection { get; set; } + public bool supports_Expel_User_Permanently { get; set; } + public bool supports_H323_DTMF { get; set; } + public bool supports_Hdmi_Cec_Control { get; set; } + public bool supports_Highly_Reverberant_Room { get; set; } + public bool supports_Loading_Contacts_Dynamically { get; set; } + public bool supports_Loading_Participants_Dynamically { get; set; } + public bool supports_Mic_Record_Test { get; set; } + public bool supports_Multi_Share { get; set; } + public bool supports_ShareCamera { get; set; } + public bool supports_Share_For_Floating_And_Content_Only { get; set; } + public bool supports_Sip_Call_out { get; set; } + public bool supports_Software_Audio_Processing { get; set; } + public bool supports_Web_Settings_Push { get; set; } + } + + public class Sharing : NotifiableObject + { + private string _dispState; + private string _password; + + public string directPresentationPairingCode { get; set; } + /// + /// Laptop client sharing key + /// + public string directPresentationSharingKey { get; set; } + public string dispState + { + get + { + return _dispState; + } + set + { + if (value != _dispState) + { + _dispState = value; + NotifyPropertyChanged("dispState"); + } + } + } + public bool isAirHostClientConnected { get; set; } + public bool isBlackMagicConnected { get; set; } + public bool isBlackMagicDataAvailable { get; set; } + public bool isDirectPresentationConnected { get; set; } + public bool isSharingBlackMagic { get; set; } + /// + /// IOS Airplay code + /// + public string password + { + get + { + return _password; + } + set + { + if (value != _password) + { + _password = value; + NotifyPropertyChanged("password"); + } + } + } + public string serverName { get; set; } + public string wifiName { get; set; } + } + + public class NumberOfScreens + { + [JsonProperty("NumberOfCECScreens")] + public int NumOfCECScreens { get; set; } + [JsonProperty("NumberOfScreens")] + public int NumOfScreens { get; set; } + } + + /// + /// AudioInputLine/AudioOutputLine/VideoCameraLine list item + /// + public class AudioVideoInputOutputLineItem + { + public string Alias { get; set; } + public string Name { get; set; } + public bool Selected { get; set; } + public bool combinedDevice { get; set; } + public string id { get; set; } + public bool manuallySelected { get; set; } + public int numberOfCombinedDevices { get; set; } + public int ptzComId { get; set; } + } + + public class Video + { + public bool Optimizable { get; set; } + } + + public class CameraShare : NotifiableObject + { + private bool _canControlCamera; + private bool _isSharing; + + [JsonProperty("can_Control_Camera")] + public bool CanControlCamera + { + get + { + return _canControlCamera; + } + set + { + if (value != _canControlCamera) + { + _canControlCamera = value; + NotifyPropertyChanged("CanControlCamera"); + } + } + } + public string id { get; set; } + public bool is_Mirrored { get; set; } + [JsonProperty("is_Sharing")] + public bool IsSharing + { + get + { + return _isSharing; + } + set + { + if (value != _isSharing) + { + _isSharing = value; + NotifyPropertyChanged("IsSharing"); + } + } + } + public int pan_Tilt_Speed { get; set; } + + } + + public class Layout + { + public bool can_Adjust_Floating_Video { get; set; } + public bool can_Switch_Floating_Share_Content { get; set; } + public bool can_Switch_Share_On_All_Screens { get; set; } + public bool can_Switch_Speaker_View { get; set; } + public bool can_Switch_Wall_View { get; set; } + public bool is_In_First_Page { get; set; } + public bool is_In_Last_Page { get; set; } + public bool is_supported { get; set; } + public int video_Count_In_Current_Page { get; set; } + public string video_type { get; set; } + } + + public class CallRecordInfo + { + public bool canRecord { get; set; } + public bool emailRequired { get; set; } + public bool amIRecording { get; set; } + public bool meetingIsBeingRecorded { get; set; } + } + } + + /// + /// zEvent Class Structure + /// + public class zEvent + { + public class NeedWaitForHost + { + public bool Wait { get; set; } + } + + public class IncomingCallIndication + { + public string callerJID { get; set; } + public string calleeJID { get; set; } + public string meetingID { get; set; } + public string password { get; set; } + public string meetingOption { get; set; } + public int MeetingNumber { get; set; } + public string callerName { get; set; } + public string avatarURL { get; set; } + public int lifeTime { get; set; } + public bool accepted { get; set; } + } + + public class CallConnectError + { + public int error_code { get; set; } + public string error_message { get; set; } + } + + public class CallDisconnect + { + public bool Successful + { + get + { + return success == "on"; + } + } + + public string success { get; set; } + } + + public class Layout + { + public bool Sharethumb { get; set; } + } + + public class Call + { + public Layout Layout { get; set; } + } + + public class Client + { + public Call Call { get; set; } + } + + public enum eSharingState + { + None, + Connecting, + Sending, + Receiving, + Send_Receiving + } + + public class SharingState : NotifiableObject + { + private bool _paused; + private eSharingState _state; + + public bool IsSharing; + + [JsonProperty("paused")] + public bool Paused + { + get + { + return _paused; + } + set + { + if (value != _paused) + { + _paused = value; + NotifyPropertyChanged("Paused"); + } + } + } + [JsonProperty("state")] + public eSharingState State + { + get + { + return _state; + } + set + { + if (value != _state) + { + _state = value; + IsSharing = _state == eSharingState.Sending; + NotifyPropertyChanged("State"); + } + } + } + } + + public class PinStatusOfScreenNotification + { + [JsonProperty("can_be_pinned")] + public bool CanBePinned { get; set; } + [JsonProperty("can_pin_share")] + public bool CanPinShare { get; set; } + [JsonProperty("pinned_share_source_id")] + public int PinnedShareSourceId { get; set; } + [JsonProperty("pinned_user_id")] + public int PinnedUserId { get; set; } + [JsonProperty("screen_index")] + public int ScreenIndex { get; set; } + [JsonProperty("screen_layout")] + public int ScreenLayout { get; set; } + [JsonProperty("share_source_type")] + public int ShareSourceType { get; set; } + [JsonProperty("why_cannot_pin_share")] + public string WhyCannotPinShare { get; set; } + } + } + + /// + /// zConfiguration class structure + /// + public class zConfiguration + { + public class Sharing + { + [JsonProperty("optimize_video_sharing")] + public bool OptimizeVideoSharing { get; set; } + } + + public class Camera + { + public bool Mute { get; set; } + } + + public class Microphone : NotifiableObject + { + private bool _mute; + + public bool Mute + { + get + { + return _mute; + } + set + { + if(value != _mute) + { + _mute = value; + NotifyPropertyChanged("Mute"); + } + } + } + } + + public enum eLayoutStyle + { + Gallery, + Speaker, + Strip, + ShareAll + } + + public enum eLayoutSize + { + Off, + Size1, + Size2, + Size3, + Strip + } + + public enum eLayoutPosition + { + Center, + Up, + Right, + UpRight, + Down, + DownRight, + Left, + UpLeft, + DownLeft + } + + public class Layout + { + public bool ShareThumb { get; set; } + public eLayoutStyle Style { get; set; } + public eLayoutSize Size { get; set; } + public eLayoutPosition Position { get; set; } + } + + public class Lock + { + public bool Enable { get; set; } + } + + public class ClosedCaption + { + public bool Visible { get; set; } + public int FontSize { get; set; } + } + + public class MuteUserOnEntry + { + public bool Enable { get; set; } + } + + public class Call + { + public Sharing Sharing { get; set; } + public Camera Camera { get; set; } + public Microphone Microphone { get; set; } + public Layout Layout { get; set; } + public Lock Lock { get; set; } + public MuteUserOnEntry MuteUserOnEntry { get; set; } + public ClosedCaption ClosedCaption { get; set; } + + + public Call() + { + Sharing = new Sharing(); + Camera = new Camera(); + Microphone = new Microphone(); + Layout = new Layout(); + Lock = new Lock(); + MuteUserOnEntry = new MuteUserOnEntry(); + ClosedCaption = new ClosedCaption(); + } + } + + public class Audio + { + public Input Input { get; set; } + public Output Output { get; set; } + + public Audio() + { + Input = new Input(); + Output = new Output(); + } + } + + public class Input : Output + { + [JsonProperty("reduce_reverb")] + public bool ReduceReverb { get; set; } + } + + public class Output : NotifiableObject + { + private int _volume; + + [JsonProperty("volume")] + public int Volume + { + get + { + return this._volume; + } + set + { + if (value != _volume) + { + this._volume = value; + NotifyPropertyChanged("Volume"); + } + } + } + [JsonProperty("selectedId")] + public string SelectedId { get; set; } + [JsonProperty("is_sap_disabled")] + public bool IsSapDisabled { get; set; } + } + + public class Video : NotifiableObject + { + private bool _hideConfSelfVideo; + + [JsonProperty("hide_conf_self_video")] + public bool HideConfSelfVideo + { + get + { + return _hideConfSelfVideo; + } + set + { + if (value != _hideConfSelfVideo) + { + _hideConfSelfVideo = value; + NotifyPropertyChanged("HideConfSelfVideo"); + } + } + } + + public VideoCamera Camera { get; set; } + + public Video() + { + Camera = new VideoCamera(); + } + } + + public class VideoCamera : NotifiableObject + { + private string _selectedId; + + [JsonProperty("selectedId")] + public string SelectedId { + get + { + return _selectedId; + } + set + { + if (value != _selectedId) + { + _selectedId = value; + NotifyPropertyChanged("SelectedId"); + } + } + + } + public bool Mirror { get; set; } + } + + public class Client + { + public string appVersion { get; set; } + public string deviceSystem { get; set; } + } + + } + + /// + /// zCommand class structure + /// + public class zCommand + { + public partial class BookingsListResult + { + [JsonProperty("accessRole")] + public string AccessRole { get; set; } + [JsonProperty("calendarChangeKey")] + public string CalendarChangeKey { get; set; } + [JsonProperty("calendarID")] + public string CalendarId { get; set; } + [JsonProperty("checkIn")] + public bool CheckIn { get; set; } + [JsonProperty("creatorEmail")] + public string CreatorEmail { get; set; } + [JsonProperty("creatorName")] + public string CreatorName { get; set; } + [JsonProperty("endTime")] + public DateTime EndTime { get; set; } + [JsonProperty("hostName")] + public string HostName { get; set; } + [JsonProperty("isInstantMeeting")] + public bool IsInstantMeeting { get; set; } + [JsonProperty("isPrivate")] + public bool IsPrivate { get; set; } + [JsonProperty("location")] + public string Location { get; set; } + [JsonProperty("meetingName")] + public string MeetingName { get; set; } + [JsonProperty("meetingNumber")] + public string MeetingNumber { get; set; } + [JsonProperty("scheduledFrom")] + public string ScheduledFrom { get; set; } + [JsonProperty("startTime")] + public DateTime StartTime { get; set; } + [JsonProperty("third_party")] + public ThirdParty ThirdParty { get; set; } + } + + /// + /// Extracts the necessary meeting values from the Cisco bookings response ans converts them to the generic class + /// + /// + /// + public static List GetGenericMeetingsFromBookingResult(List bookings) + { + var meetings = new List(); + + if (Debug.Level > 0) + { + Debug.Console(1, "Meetings List:\n"); + } + + foreach (var b in bookings) + { + var meeting = new Meeting(); + + if (b.MeetingNumber != null) + meeting.Id = b.MeetingNumber; + if (b.CreatorName != null) + meeting.Organizer = b.CreatorName; + if (b.MeetingName != null) + meeting.Title = b.MeetingName; + //if (b.Agenda != null) + // meeting.Agenda = b.Agenda.Value; + if (b.StartTime != null) + meeting.StartTime = b.StartTime; + if (b.EndTime != null) + meeting.EndTime = b.EndTime; + if (b.IsPrivate != null) + meeting.Privacy = b.IsPrivate ? eMeetingPrivacy.Private : eMeetingPrivacy.Public; + + // No meeting.Calls data exists for Zoom Rooms. Leaving out for now. + + meetings.Add(meeting); + + if (Debug.Level > 0) + { + Debug.Console(1, "Title: {0}, ID: {1}, Organizer: {2}", meeting.Title, meeting.Id, meeting.Organizer); + Debug.Console(1, " Start Time: {0}, End Time: {1}, Duration: {2}", meeting.StartTime, meeting.EndTime, meeting.Duration); + Debug.Console(1, " Joinable: {0}\n", meeting.Joinable); + } + } + + meetings.OrderBy(m => m.StartTime); + + return meetings; + } + + public class HandStatus + { + [JsonProperty("is_raise_hand")] + public bool IsRaiseHand { get; set; } + [JsonProperty("optimize_vis_validideo_sharing")] + public string IsValid { get; set; } + [JsonProperty("time_stamp")] + public string TimeStamp { get; set; } + } + + public class ListParticipant + { + [JsonProperty("audio_status state")] + public string AudioStatusState { get; set; } + [JsonProperty("audio_status type")] + public string AudioStatusType { get; set; } + [JsonProperty("avatar_url")] + public string AvatarUrl { get; set; } + [JsonProperty("camera_status am_i_controlling")] + public bool CameraStatusAmIControlling { get; set; } + [JsonProperty("camera_status can_i_request_control")] + public bool CameraStatusCanIRequestConrol { get; set; } + [JsonProperty("camera_status can_move_camera")] + public bool CameraStatusCanMoveCamera { get; set; } + [JsonProperty("camera_status can_switch_camera")] + public bool CameraStatusCanSwitchCamera { get; set; } + [JsonProperty("camera_status can_zoom_camera")] + public bool CameraStatusCanZoomCamera { get; set; } + [JsonProperty("can_edit_closed_caption")] + public bool CanEditClosedCaption { get; set; } + [JsonProperty("can_record")] + public bool CanRecord { get; set; } + [JsonProperty("event")] + public string Event { get; set; } + [JsonProperty("hand_status")] + public HandStatus HandStatus { get; set; } + [JsonProperty("isCohost")] + public bool IsCohost { get; set; } + [JsonProperty("is_client_support_closed_caption")] + public bool IsClientSupportClosedCaption { get; set; } + [JsonProperty("is_client_support_coHost")] + public bool IsClientSupportCoHost { get; set; } + [JsonProperty("is_host")] + public bool IsHost { get; set; } + [JsonProperty("is_myself")] + public bool IsMyself { get; set; } + [JsonProperty("is_recording")] + public bool IsRecording { get; set; } + [JsonProperty("is_video_can_mute_byHost")] + public bool IsVideoCanMuteByHost { get; set; } + [JsonProperty("is_video_can_unmute_byHost")] + public bool IsVideoCanUnmuteByHost { get; set; } + [JsonProperty("local_recording_disabled")] + public bool LocalRecordingDisabled { get; set; } + [JsonProperty("user_id")] + public int UserId { get; set; } + [JsonProperty("user_name")] + public string UserName { get; set; } + [JsonProperty("user_type")] + public string UserType { get; set; } + [JsonProperty("video_status has_source")] + public bool VideoStatusHasSource { get; set; } + [JsonProperty("video_status is_receiving")] + public bool VideoStatusIsReceiving { get; set; } + [JsonProperty("video_status is_sending")] + public bool VideoStatusIsSending { get; set; } + + public ListParticipant() + { + HandStatus = new HandStatus(); + } + } + + public class CallinCountryList + { + public int code { get; set; } + public string display_number { get; set; } + public string id { get; set; } + public string name { get; set; } + public string number { get; set; } + } + + public class CalloutCountryList + { + public int code { get; set; } + public string display_number { get; set; } + public string id { get; set; } + public string name { get; set; } + public string number { get; set; } + } + + public class TollFreeCallinList + { + public int code { get; set; } + public string display_number { get; set; } + public string id { get; set; } + public string name { get; set; } + public string number { get; set; } + } + + public class Info + { + public List callin_country_list { get; set; } + public List callout_country_list { get; set; } + public List toll_free_callin_list { get; set; } + } + + public class ThirdParty + { + public string h323_address { get; set; } + public string meeting_number { get; set; } + public string service_provider { get; set; } + public string sip_address { get; set; } + } + + public class MeetingListItem + { + public string accessRole { get; set; } + public string calendarChangeKey { get; set; } + public string calendarID { get; set; } + public bool checkIn { get; set; } + public string creatorEmail { get; set; } + public string creatorName { get; set; } + public string endTime { get; set; } + public string hostName { get; set; } + public bool isInstantMeeting { get; set; } + public bool isPrivate { get; set; } + public string location { get; set; } + public string meetingName { get; set; } + public string meetingNumber { get; set; } + public string scheduledFrom { get; set; } + public string startTime { get; set; } + public ThirdParty third_party { get; set; } + + public MeetingListItem() + { + third_party = new ThirdParty(); + } + } + + public class InfoResult + { + public Info Info { get; set; } + public bool am_i_original_host { get; set; } + public string default_callin_country { get; set; } + public string dialIn { get; set; } + public string international_url { get; set; } + public string invite_email_content { get; set; } + public string invite_email_subject { get; set; } + public bool is_callin_country_list_available { get; set; } + public bool is_calling_room_system_enabled { get; set; } + public bool is_toll_free_callin_list_available { get; set; } + public bool is_view_only { get; set; } + public bool is_waiting_room { get; set; } + public bool is_webinar { get; set; } + public string meeting_id { get; set; } + public MeetingListItem meeting_list_item { get; set; } + public string meeting_password { get; set; } + public string meeting_type { get; set; } + public int my_userid { get; set; } + public int participant_id { get; set; } + public string real_meeting_id { get; set; } + public string schedule_option { get; set; } + public string schedule_option2 { get; set; } + public string support_callout_type { get; set; } + public string toll_free_number { get; set; } + public string user_type { get; set; } + + public InfoResult() + { + Info = new Info(); + meeting_list_item = new MeetingListItem(); + } + } + + public class Phonebook + { + public List Contacts { get; set; } + public int Limit { get; set; } + public int Offset { get; set; } + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs new file mode 100644 index 00000000..e1fb5ee4 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs @@ -0,0 +1,1679 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.CrestronThread; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Cameras; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.Occupancy; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom +{ + public class ZoomRoom : VideoCodecBase, IHasCodecSelfView, IHasDirectory, ICommunicationMonitor, IRouting, IHasScheduleAwareness, IHasCodecCameras + { + public CommunicationGather PortGather { get; private set; } + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + private CrestronQueue ReceiveQueue; + + private Thread ReceiveThread; + + string Delimiter = "\x0D\x0A"; + + private ZoomRoomSyncState SyncState; + + public ZoomRoomStatus Status { get; private set; } + + public ZoomRoomConfiguration Configuration { get; private set; } + + private StringBuilder JsonMessage; + + private bool JsonFeedbackMessageIsIncoming; + private uint JsonCurlyBraceCounter = 0; + + public bool CommDebuggingIsOn; + + CTimer LoginMessageReceivedTimer; + CTimer RetryConnectionTimer; + + /// + /// Gets and returns the scaled volume of the codec + /// + protected override Func VolumeLevelFeedbackFunc + { + get + { + return () => CrestronEnvironment.ScaleWithLimits(Configuration.Audio.Output.Volume, 100, 0, 65535, 0); + } + } + + protected override Func PrivacyModeIsOnFeedbackFunc + { + get + { + return () => Configuration.Call.Microphone.Mute; + } + } + + protected override Func StandbyIsOnFeedbackFunc + { + get + { + return () => false; + } + } + + protected override Func SharingSourceFeedbackFunc + { + get + { + return () => Status.Sharing.dispState; + } + } + + protected override Func SharingContentIsOnFeedbackFunc + { + get + { + return () => Status.Call.Sharing.IsSharing; + } + } + + protected Func FarEndIsSharingContentFeedbackFunc + { + get + { + return () => false; + } + } + + protected override Func MuteFeedbackFunc + { + get + { + return () => Configuration.Audio.Output.Volume == 0; + } + } + + //protected Func RoomIsOccupiedFeedbackFunc + //{ + // get + // { + // return () => false; + // } + //} + + //protected Func PeopleCountFeedbackFunc + //{ + // get + // { + // return () => 0; + // } + //} + + protected Func SelfViewIsOnFeedbackFunc + { + get + { + return () => !Configuration.Video.HideConfSelfVideo; + } + } + + protected Func SelfviewPipPositionFeedbackFunc + { + get + { + return () => ""; + } + } + + protected Func LocalLayoutFeedbackFunc + { + get + { + return () => ""; + } + } + + protected Func LocalLayoutIsProminentFeedbackFunc + { + get + { + return () => false; + } + } + + + public RoutingInputPort CodecOsdIn { get; private set; } + public RoutingOutputPort Output1 { get; private set; } + + uint DefaultMeetingDurationMin = 30; + + int PreviousVolumeLevel = 0; + + public ZoomRoom(DeviceConfig config, IBasicCommunication comm) + : base(config) + { + var props = JsonConvert.DeserializeObject(config.Properties.ToString()); + + // The queue that will collect the repsonses in the order they are received + ReceiveQueue = new CrestronQueue(25); + + // The thread responsible for dequeuing and processing the messages + ReceiveThread = new Thread((o) => ProcessQueue(), null); + + Communication = comm; + + if (props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, "zStatus SystemUnit\r"); + } + + DeviceManager.AddDevice(CommunicationMonitor); + + Status = new ZoomRoomStatus(); + + Configuration = new ZoomRoomConfiguration(); + + CodecInfo = new ZoomRoomInfo(Status, Configuration); + + SyncState = new ZoomRoomSyncState(Key + "--Sync", this); + + SyncState.InitialSyncCompleted += new EventHandler(SyncState_InitialSyncCompleted); + + PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); + + PortGather = new CommunicationGather(Communication, "\x0A"); + PortGather.IncludeDelimiter = true; + PortGather.LineReceived += this.Port_LineReceived; + + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); + + Output1 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, null, this); + + SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); + + CodecSchedule = new CodecScheduleAwareness(); + + SetUpFeedbackActions(); + + Cameras = new List(); + + SetUpDirectory(); + } + + void SyncState_InitialSyncCompleted(object sender, EventArgs e) + { + SetUpRouting(); + + SetIsReady(); + } + + + /// + /// Subscribes to the PropertyChanged events on the state objects and fires the corresponding feedbacks. + /// + void SetUpFeedbackActions() + { + Configuration.Audio.Output.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( + (o, a) => + { + if (a.PropertyName == "Volume") + { + VolumeLevelFeedback.FireUpdate(); + MuteFeedback.FireUpdate(); + } + }); + + Configuration.Call.Microphone.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( + (o, a) => + { + if (a.PropertyName == "Mute") + { + PrivacyModeIsOnFeedback.FireUpdate(); + } + }); + + Configuration.Video.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( + (o, a) => + { + if (a.PropertyName == "HideConfSelfVideo") + { + SelfviewIsOnFeedback.FireUpdate(); + } + }); + Configuration.Video.Camera.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( + (o, a) => + { + if (a.PropertyName == "SelectedId") + { + SelectCamera(Configuration.Video.Camera.SelectedId); // this will in turn fire the affected feedbacks + } + }); + + Status.Call.Sharing.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( + (o, a) => + { + if (a.PropertyName == "State") + { + SharingContentIsOnFeedback.FireUpdate(); + } + }); + + Status.Sharing.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( + (o, a) => + { + if (a.PropertyName == "dispState") + { + SharingSourceFeedback.FireUpdate(); + } + else if (a.PropertyName == "password") + { + //TODO: Fire Sharing Password Update + } + }); + } + + void SetUpDirectory() + { + DirectoryRoot = new CodecDirectory(); + + DirectoryBrowseHistory = new List(); + + CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => DirectoryBrowseHistory.Count > 0); + + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + } + + void SetUpRouting() + { + // Set up input ports + CreateOsdSource(); + InputPorts.Add(CodecOsdIn); + + // Set up output ports + OutputPorts.Add(Output1); + } + + /// + /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input + /// to enable routing + /// + void CreateOsdSource() + { + OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); + DeviceManager.AddDevice(OsdSource); + var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); + TieLineCollection.Default.Add(tl); + + //foreach(var input in Status.Video. + } + + /// + /// Starts the HTTP feedback server and syncronizes state of codec + /// + /// + public override bool CustomActivate() + { + CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand((s) => SendText("zCommand Phonebook List Offset: 0 Limit: 512"), "GetZoomRoomContacts", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand((s) => GetBookings(), "GetZoomRoomBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); + + var socket = Communication as ISocketStatus; + if (socket != null) + { + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } + + // TODO: Turn this off when done initial development + CommDebuggingIsOn = true; + + Communication.Connect(); + + CommunicationMonitor.Start(); + + return base.CustomActivate(); + } + + public void SetCommDebug(string s) + { + if (s == "1") + { + CommDebuggingIsOn = true; + Debug.Console(0, this, "Comm Debug Enabled."); + } + else + { + CommDebuggingIsOn = false; + Debug.Console(0, this, "Comm Debug Disabled."); + } + } + + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus); + if (e.Client.IsConnected) + { + + } + else + { + SyncState.CodecDisconnected(); + PhonebookSyncState.CodecDisconnected(); + } + } + + public void SendText(string command) + { + if (CommDebuggingIsOn) + Debug.Console(1, this, "Sending: '{0}'", command); + + Communication.SendText(command + Delimiter); + } + + /// + /// Gathers responses and enqueues them. + /// + /// + /// + void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + //if (CommDebuggingIsOn) + // Debug.Console(1, this, "Gathered: '{0}'", args.Text); + + ReceiveQueue.Enqueue(args.Text); + + // If the receive thread has for some reason stopped, this will restart it + if (ReceiveThread.ThreadState != Thread.eThreadStates.ThreadRunning) + ReceiveThread.Start(); + } + + + /// + /// Runs in it's own thread to dequeue messages in the order they were received to be processed + /// + /// + object ProcessQueue() + { + try + { + while (true) + { + var message = ReceiveQueue.Dequeue(); + + ProcessMessage(message); + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error Processing Queue: {0}", e); + } + + return null; + } + + + /// + /// Queues the initial queries to be sent upon connection + /// + void SetUpSyncQueries() + { + // zStatus + SyncState.AddQueryToQueue("zStatus Call Status"); + SyncState.AddQueryToQueue("zStatus Audio Input Line"); + SyncState.AddQueryToQueue("zStatus Audio Output Line"); + SyncState.AddQueryToQueue("zStatus Video Camera Line"); + SyncState.AddQueryToQueue("zStatus Video Optimizable"); + SyncState.AddQueryToQueue("zStatus Capabilities"); + SyncState.AddQueryToQueue("zStatus Sharing"); + SyncState.AddQueryToQueue("zStatus CameraShare"); + SyncState.AddQueryToQueue("zStatus Call Layout"); + SyncState.AddQueryToQueue("zStatus Call ClosedCaption Available"); + SyncState.AddQueryToQueue("zStatus NumberOfScreens"); + + // zConfiguration + + SyncState.AddQueryToQueue("zConfiguration Call Sharing optimize_video_sharing"); + SyncState.AddQueryToQueue("zConfiguration Call Microphone Mute"); + SyncState.AddQueryToQueue("zConfiguration Call Camera Mute"); + SyncState.AddQueryToQueue("zConfiguration Audio Input SelectedId"); + SyncState.AddQueryToQueue("zConfiguration Audio Input is_sap_disabled"); + SyncState.AddQueryToQueue("zConfiguration Audio Input reduce_reverb"); + SyncState.AddQueryToQueue("zConfiguration Audio Input volume"); + SyncState.AddQueryToQueue("zConfiguration Audio Output selectedId"); + SyncState.AddQueryToQueue("zConfiguration Audio Output volume"); + SyncState.AddQueryToQueue("zConfiguration Video hide_conf_self_video"); + SyncState.AddQueryToQueue("zConfiguration Video Camera selectedId"); + SyncState.AddQueryToQueue("zConfiguration Video Camera Mirror"); + SyncState.AddQueryToQueue("zConfiguration Client appVersion"); + SyncState.AddQueryToQueue("zConfiguration Client deviceSystem"); + SyncState.AddQueryToQueue("zConfiguration Call Layout ShareThumb"); + SyncState.AddQueryToQueue("zConfiguration Call Layout Style"); + SyncState.AddQueryToQueue("zConfiguration Call Layout Size"); + SyncState.AddQueryToQueue("zConfiguration Call Layout Position"); + SyncState.AddQueryToQueue("zConfiguration Call Lock Enable"); + SyncState.AddQueryToQueue("zConfiguration Call MuteUserOnEntry Enable"); + SyncState.AddQueryToQueue("zConfiguration Call ClosedCaption FontSize "); + SyncState.AddQueryToQueue("zConfiguration Call ClosedCaption Visible"); + + // zCommand + + SyncState.AddQueryToQueue("zCommand Phonebook List Offset: 0 Limit: 512"); + SyncState.AddQueryToQueue("zCommand Bookings List"); + + + SyncState.StartSync(); + } + + /// + /// Processes messages as they are dequeued + /// + /// + void ProcessMessage(string message) + { + // Counts the curly braces + if(message.Contains('{')) + JsonCurlyBraceCounter++; + + if (message.Contains('}')) + JsonCurlyBraceCounter--; + + Debug.Console(2, this, "JSON Curly Brace Count: {0}", JsonCurlyBraceCounter); + + if (!JsonFeedbackMessageIsIncoming && message.Trim('\x20') == "{" + Delimiter) // Check for the beginning of a new JSON message + { + JsonFeedbackMessageIsIncoming = true; + JsonCurlyBraceCounter = 1; // reset the counter for each new message + + JsonMessage = new StringBuilder(); + + JsonMessage.Append(message); + + if (CommDebuggingIsOn) + Debug.Console(2, this, "Incoming JSON message..."); + + return; + } + else if (JsonFeedbackMessageIsIncoming && message.Trim('\x20') == "}" + Delimiter) // Check for the end of a JSON message + { + JsonMessage.Append(message); + + if(JsonCurlyBraceCounter == 0) + { + JsonFeedbackMessageIsIncoming = false; + + if (CommDebuggingIsOn) + Debug.Console(2, this, "Complete JSON Received:\n{0}", JsonMessage.ToString()); + + // Forward the complete message to be deserialized + DeserializeResponse(JsonMessage.ToString()); + } + + //JsonMessage = new StringBuilder(); + return; + } + + // NOTE: This must happen after the above conditions have been checked + // Append subsequent partial JSON fragments to the string builder + if (JsonFeedbackMessageIsIncoming) + { + JsonMessage.Append(message); + + //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); + return; + } + + if (CommDebuggingIsOn) + Debug.Console(1, this, "Non-JSON response: '{0}'", message); + + JsonCurlyBraceCounter = 0; // reset on non-JSON response + + if (!SyncState.InitialSyncComplete) + { + switch (message.Trim().ToLower()) // remove the whitespace + { + case "*r login successful": + { + SyncState.LoginMessageReceived(); + + // Fire up a thread to send the intial commands. + CrestronInvoke.BeginInvoke((o) => + { + Thread.Sleep(100); + // disable echo of commands + SendText("echo off"); + Thread.Sleep(100); + // set feedback exclusions + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callin_country_list"); + Thread.Sleep(100); + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callout_country_list"); + Thread.Sleep(100); + // switch to json format + SendText("format json"); + }); + + break; + } + } + } + } + + /// + /// Deserializes a JSON formatted response + /// + /// + void DeserializeResponse(string response) + { + try + { + var trimmedResponse = response.Trim(); + + if (trimmedResponse.Length <= 0) + return; + + var message = JObject.Parse(trimmedResponse); + + eZoomRoomResponseType eType = (eZoomRoomResponseType)Enum.Parse(typeof(eZoomRoomResponseType), message["type"].Value(), true); + + var topKey = message["topKey"].Value(); + + var responseObj = message[topKey]; + + Debug.Console(1, "{0} Response Received. topKey: '{1}'\n{2}", eType, topKey, responseObj.ToString()); + + switch (eType) + { + case eZoomRoomResponseType.zConfiguration: + { + switch (topKey.ToLower()) + { + case "call": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Call); + + break; + } + case "audio": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Audio); + + break; + } + case "video": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Video); + + break; + } + case "client": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Client); + + break; + } + default: + { + break; + } + } + break; + } + case eZoomRoomResponseType.zCommand: + { + switch (topKey.ToLower()) + { + case "phonebooklistresult": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Phonebook); + + if(!PhonebookSyncState.InitialSyncComplete) + { + PhonebookSyncState.InitialPhonebookFoldersReceived(); + PhonebookSyncState.PhonebookRootEntriesReceived(); + PhonebookSyncState.SetPhonebookHasFolders(false); + PhonebookSyncState.SetNumberOfContacts(Status.Phonebook.Contacts.Count); + } + + var directoryResults = new CodecDirectory(); + + directoryResults = zStatus.Phonebook.ConvertZoomContactsToGeneric(Status.Phonebook.Contacts); + + DirectoryRoot = directoryResults; + + OnDirectoryResultReturned(directoryResults); + + break; + } + case "listparticipantsresult": + { + Debug.Console(1, this, "JTokenType: {0}", responseObj.Type); + + if (responseObj.Type == JTokenType.Array) + { + // if the type is array this must be the complete list + Status.Call.Participants = JsonConvert.DeserializeObject>(responseObj.ToString()); + } + else if (responseObj.Type == JTokenType.Object) + { + // this is a single participant event notification + + var participant = JsonConvert.DeserializeObject(responseObj.ToString()); + + if (participant != null) + { + if (participant.Event == "ZRCUserChangedEventLeftMeeting" || participant.Event == "ZRCUserChangedEventUserInfoUpdated") + { + var existingParticipant = Status.Call.Participants.FirstOrDefault(p => p.UserId.Equals(participant.UserId)); + + if (existingParticipant != null) + { + if (participant.Event == "ZRCUserChangedEventLeftMeeting") + { + // Remove participant + Status.Call.Participants.Remove(existingParticipant); + } + else if (participant.Event == "ZRCUserChangedEventUserInfoUpdated") + { + // Update participant + JsonConvert.PopulateObject(responseObj.ToString(), existingParticipant); + } + } + } + else if(participant.Event == "ZRCUserChangedEventJoinedMeeting") + { + Status.Call.Participants.Add(participant); + } + } + } + + PrintCurrentCallParticipants(); + + break; + } + default: + { + break; + } + } + break; + } + case eZoomRoomResponseType.zEvent: + { + switch (topKey.ToLower()) + { + case "phonebook": + { + if (responseObj["Updated Contact"] != null) + { + var updatedContact = JsonConvert.DeserializeObject(responseObj["Updated Contact"].ToString()); + + var existingContact = Status.Phonebook.Contacts.FirstOrDefault(c => c.Jid.Equals(updatedContact.Jid)); + + if (existingContact != null) + { + // Update existing contact + JsonConvert.PopulateObject(responseObj["Updated Contact"].ToString(), existingContact); + } + } + else if (responseObj["Added Contact"] != null) + { + var newContact = JsonConvert.DeserializeObject(responseObj["Updated Contact"].ToString()); + + // Add a new contact + Status.Phonebook.Contacts.Add(newContact); + } + + break; + } + case "bookingslistresult": + { + if (!SyncState.InitialSyncComplete) + SyncState.LastQueryResponseReceived(); + + var codecBookings = new List(); + + codecBookings = JsonConvert.DeserializeObject < List>(responseObj.ToString()); + + if (codecBookings != null && codecBookings.Count > 0) + { + CodecSchedule.Meetings = zCommand.GetGenericMeetingsFromBookingResult(codecBookings); + } + + break; + } + case "bookings": + { + // Bookings have been updated, trigger a query to retreive the new bookings + if (responseObj["Updated"] != null) + GetBookings(); + + break; + } + case "sharingstate": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Sharing); + + break; + } + case "incomingcallindication": + { + var incomingCall = JsonConvert.DeserializeObject(responseObj.ToString()); + + if (incomingCall != null) + { + var newCall = new CodecActiveCallItem(); + + newCall.Direction = eCodecCallDirection.Incoming; + newCall.Status = eCodecCallStatus.Ringing; + newCall.Type = eCodecCallType.Unknown; + newCall.Name = incomingCall.callerName; + newCall.Id = incomingCall.callerJID; + + ActiveCalls.Add(newCall); + + OnCallStatusChange(newCall); + } + + break; + } + case "treatedincomingcallindication": + { + var incomingCall = JsonConvert.DeserializeObject(responseObj.ToString()); + + if (incomingCall != null) + { + var existingCall = ActiveCalls.FirstOrDefault(c => c.Id.Equals(incomingCall.callerJID)); + + if (existingCall != null) + { + if (!incomingCall.accepted) + { + existingCall.Status = eCodecCallStatus.Disconnected; + } + else + { + existingCall.Status = eCodecCallStatus.Connecting; + } + + OnCallStatusChange(existingCall); + } + + UpdateCallStatus(); + } + + break; + } + case "calldisconnect": + { + var disconnectEvent = JsonConvert.DeserializeObject(responseObj.ToString()); + + if (disconnectEvent.Successful) + { + if (ActiveCalls.Count > 0) + { + var activeCall = ActiveCalls.FirstOrDefault(c => c.IsActiveCall); + + if (activeCall != null) + { + activeCall.Status = eCodecCallStatus.Disconnected; + + OnCallStatusChange(activeCall); + } + } + } + + UpdateCallStatus(); + break; + } + case "callconnecterror": + { + UpdateCallStatus(); + break; + } + case "videounmuterequest": + { + // TODO: notify room of a request to unmute video + break; + } + case "meetingneedspassword": + { + // TODO: notify user to enter a password + break; + } + case "needwaitforhost": + { + var needWait = JsonConvert.DeserializeObject(responseObj.ToString()); + + if (needWait.Wait) + { + // TODO: notify user to wait for host + } + + break; + } + case "openvideofailforhoststop": + { + // TODO: notify user that host has disabled unmuting video + break; + } + case "updatedcallrecordinfo": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.CallRecordInfo); + + break; + } + default: + { + break; + } + } + break; + } + case eZoomRoomResponseType.zStatus: + { + switch (topKey.ToLower()) + { + case "login": + { + SyncState.LoginMessageReceived(); + + if (!SyncState.InitialQueryMessagesWereSent) + SetUpSyncQueries(); + + JsonConvert.PopulateObject(responseObj.ToString(), Status.Login); + + break; + } + case "systemunit": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.SystemUnit); + + break; + } + case "call": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call); + + UpdateCallStatus(); + + break; + } + case "capabilities": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Capabilities); + break; + } + case "sharing": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Sharing); + + break; + } + case "numberofscreens": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.NumberOfScreens); + break; + } + case "video": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Video); + break; + } + case "camerashare": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.CameraShare); + break; + } + case "layout": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Layout); + break; + } + case "audio input line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioInputs); + break; + } + case "audio output line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioOuputs); + break; + } + case "video camera line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Cameras); + + if(!SyncState.CamerasHaveBeenSetUp) + SetUpCameras(); + + break; + } + default: + { + break; + } + } + + break; + } + default: + { + Debug.Console(1, "Unknown Response Type:"); + break; + } + } + + } + catch (Exception ex) + { + Debug.Console(1, this, "Error Deserializing feedback: {0}", ex); + } + } + + public void PrintCurrentCallParticipants() + { + if (Debug.Level > 0) + { + Debug.Console(1, this, "****************************Call Participants***************************"); + foreach (var participant in Status.Call.Participants) + { + Debug.Console(1, this, "Name: {0} Audio: {1} IsHost: {2}", participant.UserName, participant.AudioStatusState, participant.IsHost); + } + Debug.Console(1, this, "************************************************************************"); + } + } + + /// + /// Retrieves bookings list + /// + void GetBookings() + { + SendText("zCommand Bookings List"); + } + + + /// + /// Updates the current call status + /// + void UpdateCallStatus() + { + zStatus.eCallStatus callStatus; + + if (Status.Call != null) + { + callStatus = Status.Call.Status; + + // If not currently in a meeting, intialize the call object + if (callStatus != zStatus.eCallStatus.IN_MEETING || callStatus != zStatus.eCallStatus.CONNECTING_MEETING) + { + Status.Call = new zStatus.Call(); + Status.Call.Status = callStatus; // set the status after initializing the object + } + + if (ActiveCalls.Count == 0) + { + if(callStatus == zStatus.eCallStatus.CONNECTING_MEETING) + { + var newCall = new CodecActiveCallItem(); + + newCall.Status = eCodecCallStatus.Connecting; + + ActiveCalls.Add(newCall); + + OnCallStatusChange(newCall); + } + } + else + { + var existingCall = ActiveCalls.FirstOrDefault(c => !c.Status.Equals(eCodecCallStatus.Ringing)); + + if (callStatus == zStatus.eCallStatus.IN_MEETING) + { + existingCall.Status = eCodecCallStatus.Connected; + } + else if (callStatus == zStatus.eCallStatus.NOT_IN_MEETING) + { + existingCall.Status = eCodecCallStatus.Disconnected; + } + + OnCallStatusChange(existingCall); + } + + } + + Debug.Console(1, this, "****************************Active Calls*********************************"); + + // Clean up any disconnected calls left in the list + for (int i = 0; i < ActiveCalls.Count; i++) + { + var call = ActiveCalls[i]; + + Debug.Console(1, this, + @"Name: {0} + ID: {1} + IsActive: {2} + Status: {3} + Direction: {4}", call.Name, call.Id, call.IsActiveCall, call.Status, call.Direction); + + if (!call.IsActiveCall) + { + Debug.Console(1, this, "******Removing Inactive Call: {0}******", call.Name); + ActiveCalls.Remove(call); + } + } + + Debug.Console(1, this, "**************************************************************************"); + + } + + public override void StartSharing() + { + throw new NotImplementedException(); + } + + /// + /// Stops sharing the current presentation + /// + public override void StopSharing() + { + SendText("zCommand Call Sharing Disconnect"); + } + + public override void PrivacyModeOn() + { + SendText("zConfiguration Call Microphone Mute: on"); + } + + public override void PrivacyModeOff() + { + SendText("zConfiguration Call Microphone Mute: off"); + } + + public override void PrivacyModeToggle() + { + if (PrivacyModeIsOnFeedback.BoolValue) + PrivacyModeOff(); + else + PrivacyModeOn(); + } + + public override void MuteOff() + { + SetVolume((ushort)PreviousVolumeLevel); + } + + public override void MuteOn() + { + PreviousVolumeLevel = Configuration.Audio.Output.Volume; // Store the previous level for recall + + SetVolume(0); + } + + public override void MuteToggle() + { + if (MuteFeedback.BoolValue) + MuteOff(); + else + MuteOn(); + } + + /// + /// Increments the voluem + /// + /// + public override void VolumeUp(bool pressRelease) + { + // TODO: Implment volume increment that calls SetVolume() + } + + /// + /// Decrements the volume + /// + /// + public override void VolumeDown(bool pressRelease) + { + // TODO: Implment volume decrement that calls SetVolume() + } + + /// + /// Scales the level and sets the codec to the specified level within its range + /// + /// level from slider (0-65535 range) + public override void SetVolume(ushort level) + { + var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0); + SendText(string.Format("zConfiguration Audio Output volume: {0}", scaledLevel)); + } + + /// + /// Recalls the default volume on the codec + /// + public void VolumeSetToDefault() + { + + } + + /// + /// + /// + public override void StandbyActivate() + { + // No corresponding function on device + } + + /// + /// + /// + public override void StandbyDeactivate() + { + // No corresponding function on device + } + + public override void ExecuteSwitch(object selector) + { + (selector as Action)(); + } + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + ExecuteSwitch(inputSelector); + } + + public override void AcceptCall(CodecActiveCallItem call) + { + var incomingCall = ActiveCalls.FirstOrDefault(c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); + SendText(string.Format("zCommand Call Accept callerJID: {0}", incomingCall.Id)); + } + + public override void RejectCall(CodecActiveCallItem call) + { + var incomingCall = ActiveCalls.FirstOrDefault(c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); + SendText(string.Format("zCommand Call Reject callerJID: {0}", incomingCall.Id)); + } + + public override void Dial(Meeting meeting) + { + SendText(string.Format("zCommand Dial Start meetingNumber: {0}", meeting.Id)); + } + + public override void Dial(string number) + { + SendText(string.Format("zCommand Dial Join meetingNumber: {0}", number)); + } + + /// + /// Invites a contact to either a new meeting (if not already in a meeting) or the current meeting. + /// Currently only invites a single user + /// + /// + public override void Dial(IInvitableContact contact) + { + var ic = contact as zStatus.ZoomDirectoryContact; + + if (ic != null) + { + Debug.Console(1, this, "Attempting to Dial (Invite): {0}", ic.Name); + + if (!IsInCall) + SendText(string.Format("zCommand Invite Duration: {0} user: {1}", DefaultMeetingDurationMin, ic.ContactId)); + else + SendText(string.Format("zCommand Call invite user: {0}", ic.ContactId)); + } + } + + public override void EndCall(CodecActiveCallItem call) + { + SendText("zCommand Call Disconnect"); + } + + public override void EndAllCalls() + { + SendText("zCommand Call Disconnect"); + } + + public override void SendDtmf(string s) + { + throw new NotImplementedException(); + } + + + #region IHasCodecSelfView Members + + public BoolFeedback SelfviewIsOnFeedback { get; private set; } + + public void SelfViewModeOn() + { + SendText("zConfiguration Video hide_conf_self_video: off"); + } + + public void SelfViewModeOff() + { + SendText("zConfiguration Video hide_conf_self_video: on"); + } + + public void SelfViewModeToggle() + { + if (SelfviewIsOnFeedback.BoolValue) + SelfViewModeOff(); + else + SelfViewModeOn(); + } + + #endregion + + #region IHasDirectory Members + + public event EventHandler DirectoryResultReturned; + + /// Call when directory results are updated + /// + /// + void OnDirectoryResultReturned(CodecDirectory result) + { + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + + // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology + var handler = DirectoryResultReturned; + if (handler != null) + { + handler(this, new DirectoryEventArgs() + { + Directory = result, + DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue + }); + } + + //PrintDirectory(result); + } + + public CodecDirectory DirectoryRoot { get; private set; } + + public CodecDirectory CurrentDirectoryResult + { + get + { + if (DirectoryBrowseHistory.Count > 0) + return DirectoryBrowseHistory[DirectoryBrowseHistory.Count - 1]; + else + return DirectoryRoot; + } + } + + public CodecPhonebookSyncState PhonebookSyncState { get; private set; } + + public void SearchDirectory(string searchString) + { + var directoryResults = new CodecDirectory(); + + directoryResults.AddContactsToDirectory(DirectoryRoot.CurrentDirectoryResults.FindAll(c => c.Name.IndexOf(searchString, 0, StringComparison.OrdinalIgnoreCase) > -1)); + + DirectoryBrowseHistory.Add(directoryResults); + + OnDirectoryResultReturned(directoryResults); + } + + public void GetDirectoryFolderContents(string folderId) + { + var directoryResults = new CodecDirectory(); + + directoryResults.ResultsFolderId = folderId; + directoryResults.AddContactsToDirectory(DirectoryRoot.CurrentDirectoryResults.FindAll(c => c.FolderId.Equals(folderId))); + + DirectoryBrowseHistory.Add(directoryResults); + + OnDirectoryResultReturned(directoryResults); + } + + public void SetCurrentDirectoryToRoot() + { + DirectoryBrowseHistory.Clear(); + + OnDirectoryResultReturned(DirectoryRoot); + } + + public void GetDirectoryParentFolderContents() + { + var currentDirectory = new CodecDirectory(); + + if (DirectoryBrowseHistory.Count > 0) + { + var lastItemIndex = DirectoryBrowseHistory.Count - 1; + var parentDirectoryContents = DirectoryBrowseHistory[lastItemIndex]; + + DirectoryBrowseHistory.Remove(DirectoryBrowseHistory[lastItemIndex]); + + currentDirectory = parentDirectoryContents; + + } + else + { + currentDirectory = DirectoryRoot; + } + + OnDirectoryResultReturned(currentDirectory); + } + + public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } + + public List DirectoryBrowseHistory { get; private set; } + + #endregion + + #region IHasScheduleAwareness Members + + public CodecScheduleAwareness CodecSchedule { get; private set; } + + public void GetSchedule() + { + GetBookings(); + } + + #endregion + + /// + /// Builds the cameras List by using the Zoom Room zStatus.Cameras data. Could later be modified to build from config data + /// + void SetUpCameras() + { + SelectedCameraFeedback = new StringFeedback(() => Configuration.Video.Camera.SelectedId); + + ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera); + + foreach (var cam in Status.Cameras) + { + var camera = new ZoomRoomCamera(cam.id, cam.Name, this); + + Cameras.Add(camera); + + if (cam.Selected) + SelectedCamera = camera; + } + + if (IsInCall) + UpdateFarEndCameras(); + + SyncState.CamerasSetUp(); + } + + /// + /// Dynamically creates far end cameras for call participants who have far end control enabled. + /// + void UpdateFarEndCameras() + { + // TODO: set up far end cameras for the current call + } + + #region IHasCameras Members + + public event EventHandler CameraSelected; + + public List Cameras { get; private set; } + + private CameraBase _selectedCamera; + + public CameraBase SelectedCamera + { + get + { + return _selectedCamera; + } + private set + { + _selectedCamera = value; + SelectedCameraFeedback.FireUpdate(); + ControllingFarEndCameraFeedback.FireUpdate(); + + var handler = CameraSelected; + if (handler != null) + { + handler(this, new CameraSelectedEventArgs(SelectedCamera)); + } + } + } + + + public StringFeedback SelectedCameraFeedback { get; private set; } + + public void SelectCamera(string key) + { + if(Cameras != null) + { + var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1); + if (camera != null) + { + Debug.Console(1, this, "Selected Camera with key: '{0}'", camera.Key); + SelectedCamera = camera; + } + else + Debug.Console(1, this, "Unable to select camera with key: '{0}'", key); + } + } + + #endregion + + #region IHasFarEndCameraControl Members + + public CameraBase FarEndCamera { get; private set; } + + public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } + + #endregion + } + + /// + /// Zoom Room specific info object + /// + public class ZoomRoomInfo : VideoCodecInfo + { + public ZoomRoomStatus Status { get; private set; } + public ZoomRoomConfiguration Configuration { get; private set; } + + public override bool AutoAnswerEnabled + { + get + { + return Status.SystemUnit.RoomInfo.AutoAnswerIsEnabled; + } + } + + public override string E164Alias + { + get + { + if (!string.IsNullOrEmpty(Status.SystemUnit.MeetingNumber)) + return Status.SystemUnit.MeetingNumber; + else + return string.Empty; + } + } + + public override string H323Id + { + get + { + if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.h323_address)) + return Status.Call.Info.meeting_list_item.third_party.h323_address; + else + return string.Empty; + } + } + + public override string IpAddress + { + get + { + if (!string.IsNullOrEmpty(Status.SystemUnit.RoomInfo.AccountEmail)) + return Status.SystemUnit.RoomInfo.AccountEmail; + else + return string.Empty; + } + } + + public override bool MultiSiteOptionIsEnabled + { + get { return true; } + } + + public override string SipPhoneNumber + { + get + { + if (!string.IsNullOrEmpty(Status.Call.Info.dialIn)) + return Status.Call.Info.dialIn; + else + return string.Empty; + } + } + + public override string SipUri + { + get + { + if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.sip_address)) + return Status.Call.Info.meeting_list_item.third_party.sip_address; + else + return string.Empty; + } + } + + public ZoomRoomInfo(ZoomRoomStatus status, ZoomRoomConfiguration configuration) + { + Status = status; + Configuration = configuration; + } + } + + /// + /// Tracks the initial sycnronization state when establishing a new connection + /// + public class ZoomRoomSyncState : IKeyed + { + bool _InitialSyncComplete; + + public event EventHandler InitialSyncCompleted; + + private CrestronQueue SyncQueries; + + private ZoomRoom Parent; + + public string Key { get; private set; } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + handler(this, new EventArgs()); + } + _InitialSyncComplete = value; + } + } + + public bool LoginMessageWasReceived { get; private set; } + + public bool InitialQueryMessagesWereSent { get; private set; } + + public bool LastQueryResponseWasReceived { get; private set; } + + public bool CamerasHaveBeenSetUp { get; private set;} + + public ZoomRoomSyncState(string key, ZoomRoom parent) + { + Parent = parent; + Key = key; + SyncQueries = new CrestronQueue(50); + CodecDisconnected(); + } + + public void StartSync() + { + DequeueQueries(); + } + + void DequeueQueries() + { + while (!SyncQueries.IsEmpty) + { + var query = SyncQueries.Dequeue(); + + Parent.SendText(query); + } + + InitialQueryMessagesSent(); + } + + public void AddQueryToQueue(string query) + { + SyncQueries.Enqueue(query); + } + + public void LoginMessageReceived() + { + LoginMessageWasReceived = true; + Debug.Console(1, this, "Login Message Received."); + CheckSyncStatus(); + } + + public void InitialQueryMessagesSent() + { + InitialQueryMessagesWereSent = true; + Debug.Console(1, this, "Query Messages Sent."); + CheckSyncStatus(); + } + + public void LastQueryResponseReceived() + { + LastQueryResponseWasReceived = true; + Debug.Console(1, this, "Last Query Response Received."); + CheckSyncStatus(); + } + + public void CamerasSetUp() + { + CamerasHaveBeenSetUp = true; + Debug.Console(1, this, "Cameras Set Up."); + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + SyncQueries.Clear(); + LoginMessageWasReceived = false; + InitialQueryMessagesWereSent = false; + LastQueryResponseWasReceived = false; + CamerasHaveBeenSetUp = false; + InitialSyncComplete = false; + } + + void CheckSyncStatus() + { + if (LoginMessageWasReceived && InitialQueryMessagesWereSent && LastQueryResponseWasReceived && CamerasHaveBeenSetUp) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Codec Sync Complete!"); + } + else + InitialSyncComplete = false; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs new file mode 100644 index 00000000..32a8296e --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomCamera.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Devices.Common.Cameras; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom +{ + public enum eZoomRoomCameraState + { + Start, + Continue, + Stop, + RequestRemote, + GiveupRemote, + RequestedByFarEnd + } + + public enum eZoomRoomCameraAction + { + Left, + Right, + Up, + Down, + In, + Out + } + + + public class ZoomRoomCamera : CameraBase, IHasCameraPtzControl + { + protected ZoomRoom ParentCodec { get; private set; } + + public int Id = 0; // ID of near end selected camara is always 0 + + private int ContinueTime = 10; // number of milliseconds between issuing continue commands + + private CTimer ContinueTimer; + + eZoomRoomCameraAction LastAction; + + private bool isPanning; + + private bool isTilting; + + private bool isZooming; + + private bool isFocusing; + + private bool isMoving + { + get + { + return isPanning || isTilting || isZooming || isFocusing; + + } + } + + public ZoomRoomCamera(string key, string name, ZoomRoom codec) + : base(key, name) + { + ParentCodec = codec; + } + + /// + /// Builds the command and triggers the parent ZoomRoom to send it + /// + /// + /// + /// + void SendCommand(eZoomRoomCameraState state, eZoomRoomCameraAction action) + { + LastAction = action; + ParentCodec.SendText(string.Format("zCommand Call CameraControl Id: {0} State: {1} Action: {2}", Id, state, action)); + } + + void StartContinueTimer() + { + if(ContinueTimer == null) + ContinueTimer = new CTimer((o) => SendContinueAction(LastAction), ContinueTime); + } + + void SendContinueAction(eZoomRoomCameraAction action) + { + SendCommand(eZoomRoomCameraState.Continue, action); + ContinueTimer.Reset(); + } + + void StopContinueTimer() + { + if (ContinueTimer != null) + { + ContinueTimer.Stop(); + ContinueTimer.Dispose(); + } + } + + #region IHasCameraPtzControl Members + + public void PositionHome() + { + throw new NotImplementedException(); + } + + #endregion + + #region IHasCameraPanControl Members + + public void PanLeft() + { + if (!isMoving) + { + SendCommand(eZoomRoomCameraState.Start, eZoomRoomCameraAction.Left); + StartContinueTimer(); + isPanning = true; + } + } + + public void PanRight() + { + if (!isMoving) + { + SendCommand(eZoomRoomCameraState.Start, eZoomRoomCameraAction.Right); + StartContinueTimer(); + isPanning = true; + } + } + + public void PanStop() + { + StopContinueTimer(); + SendCommand(eZoomRoomCameraState.Stop, LastAction); + isPanning = false; + } + + #endregion + + #region IHasCameraTiltControl Members + + public void TiltDown() + { + if (!isMoving) + { + SendCommand(eZoomRoomCameraState.Start, eZoomRoomCameraAction.Down); + StartContinueTimer(); + isTilting = true; + } + } + + public void TiltUp() + { + if (!isMoving) + { + SendCommand(eZoomRoomCameraState.Start, eZoomRoomCameraAction.Up); + StartContinueTimer(); + isTilting = true; + } + } + + public void TiltStop() + { + StopContinueTimer(); + SendCommand(eZoomRoomCameraState.Stop, LastAction); + isTilting = false; + } + + #endregion + + #region IHasCameraZoomControl Members + + public void ZoomIn() + { + if (!isMoving) + { + SendCommand(eZoomRoomCameraState.Start, eZoomRoomCameraAction.In); + StartContinueTimer(); + isZooming = true; + } + } + + public void ZoomOut() + { + if (!isMoving) + { + SendCommand(eZoomRoomCameraState.Start, eZoomRoomCameraAction.Out); + StartContinueTimer(); + isZooming = true; + } + } + + public void ZoomStop() + { + StopContinueTimer(); + SendCommand(eZoomRoomCameraState.Stop, LastAction); + isZooming = false; + } + + #endregion + } + + public class ZoomRoomFarEndCamera : ZoomRoomCamera, IAmFarEndCamera + { + + public ZoomRoomFarEndCamera(string key, string name, ZoomRoom codec, int id) + : base(key, name, codec) + { + Id = id; + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs new file mode 100644 index 00000000..d60b63c2 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom +{ + public class ZoomRoomPropertiesConfig + { + public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Framework.sln b/essentials-framework/Essentials Framework.sln new file mode 100644 index 00000000..c508ad29 --- /dev/null +++ b/essentials-framework/Essentials Framework.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash_Essentials_Core", ".\Essentials Core\PepperDashEssentialsBase\PepperDash_Essentials_Core.csproj", "{A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Essentials_DM", ".\Essentials DM\Essentials_DM\Essentials_DM.csproj", "{9199CE8A-0C9F-4952-8672-3EED798B284F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Essentials Devices Common", ".\Essentials Devices Common\Essentials Devices Common\Essentials Devices Common.csproj", "{892B761C-E479-44CE-BD74-243E9214AF13}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5}.Release|Any CPU.Build.0 = Release|Any CPU + {9199CE8A-0C9F-4952-8672-3EED798B284F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9199CE8A-0C9F-4952-8672-3EED798B284F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9199CE8A-0C9F-4952-8672-3EED798B284F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9199CE8A-0C9F-4952-8672-3EED798B284F}.Release|Any CPU.Build.0 = Release|Any CPU + {892B761C-E479-44CE-BD74-243E9214AF13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {892B761C-E479-44CE-BD74-243E9214AF13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {892B761C-E479-44CE-BD74-243E9214AF13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {892B761C-E479-44CE-BD74-243E9214AF13}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/essentials-framework/Essentials/PepperDashEssentials/Bridges/Bridges.BridgeFactory.cs b/essentials-framework/Essentials/PepperDashEssentials/Bridges/Bridges.BridgeFactory.cs new file mode 100644 index 00000000..f65d4e65 --- /dev/null +++ b/essentials-framework/Essentials/PepperDashEssentials/Bridges/Bridges.BridgeFactory.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Core; +using PepperDash.Essentials.Core.Routing; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.EthernetCommunication; + +namespace PepperDash.Essentials { + public class BridgeFactory { + public static IKeyed GetDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc) { + // ? why is this static JTA 2018-06-13? + + var key = dc.Key; + var name = dc.Name; + var type = dc.Type; + var properties = dc.Properties; + var propAnon = new { }; + JsonConvert.DeserializeAnonymousType(dc.Properties.ToString(), propAnon); + + var typeName = dc.Type.ToLower(); + var groupName = dc.Group.ToLower(); + + Debug.Console(0, "Name {0}, Key {1}, Type {2}, Properties {3}", name, key, type, properties.ToString()); + if (typeName == "essentialdm") { + return new EssentialDM(key, name, properties); + } else if (typeName == "essentialcomm") { + + Debug.Console(0, "Launch Essential Comm"); + return new EssentialComm(key, name, properties); + + } + return null; + } + } + public class BridgeApiEisc { + public uint Ipid; + public ThreeSeriesTcpIpEthernetIntersystemCommunications Eisc; + public BridgeApiEisc(string ipid) { + Ipid = (UInt32)int.Parse(ipid, System.Globalization.NumberStyles.HexNumber); + Eisc = new ThreeSeriesTcpIpEthernetIntersystemCommunications(Ipid, "127.0.0.2", Global.ControlSystem); + Eisc.Register(); + Eisc.SigChange += Eisc_SigChange; + Debug.Console(0, "BridgeApiEisc Created at Ipid {0}", ipid); + } + void Eisc_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args) { + if (Debug.Level >= 1) + Debug.Console(1, "DDVC EISC change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); + var uo = args.Sig.UserObject; + if (uo is Action) + (uo as Action)(args.Sig.BoolValue); + else if (uo is Action) + (uo as Action)(args.Sig.UShortValue); + else if (uo is Action) + (uo as Action)(args.Sig.StringValue); + } + } + + } + + + + \ No newline at end of file diff --git a/essentials-framework/Essentials/PepperDashEssentials/Bridges/EssentialComms.cs b/essentials-framework/Essentials/PepperDashEssentials/Bridges/EssentialComms.cs new file mode 100644 index 00000000..9102a1e2 --- /dev/null +++ b/essentials-framework/Essentials/PepperDashEssentials/Bridges/EssentialComms.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Core; +using PepperDash.Essentials.Core.Routing; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.EthernetCommunication; +using Crestron.SimplSharpPro.CrestronThread; + +namespace PepperDash.Essentials { + public class EssentialCommConfig { + public string[] EiscApiIpids; + public EssentialCommCommConnectionConfigs[] CommConnections; + } + public class EssentialCommCommConnectionConfigs { + public uint joinNumber {get; set; } + public EssentialsControlPropertiesConfig control { get; set; } + } + + public class EssentialCommsPort { + public IBasicCommunication Comm; + public IntFeedback StatusFeedback; + public BoolFeedback ConnectedFeedback; + public List Outputs = new List(); + public String RxBuffer; + public EssentialCommsPort(EssentialsControlPropertiesConfig config, string keyPrefix) { + Comm = CommFactory.CreateCommForConfig(config, keyPrefix); + // var PortGather = new CommunicationGather(Comm, config.EndOfLineChar); + Comm.TextReceived += new EventHandler(Communication_TextReceived); + + var socket = Comm as ISocketStatus; + StatusFeedback = new IntFeedback(() => { return (int)socket.ClientStatus; }); + ConnectedFeedback = new BoolFeedback(() => { return Comm.IsConnected; }); + + if (socket != null) { + socket.ConnectionChange += new EventHandler(socket_ConnectionChange); + } else { + } + + } + void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) { + StatusFeedback.FireUpdate(); + ConnectedFeedback.FireUpdate(); + if (e.Client.IsConnected) { + // Tasks on connect + } else { + // Cleanup items from this session + } + } + void Communication_TextReceived(object sender, GenericCommMethodReceiveTextArgs args) { + try { + foreach (var Output in Outputs) { + Output.Api.Eisc.StringInput[Output.Join].StringValue = args.Text; + } + + } + catch (Exception) { + throw new FormatException(string.Format("ERROR:{0}")); + } + } + } + + public class EssentialComm : Device { + public EssentialCommConfig Properties; + public String Key; + public CommunicationGather PortGather { get; private set; } + public List Apis {get; set;} + public Dictionary CommFeedbacks {get; private set; } + public StatusMonitorBase CommunicationMonitor { get; private set; } + public Dictionary CommDictionary { get; private set; } + + public EssentialComm(string key, string name, JToken properties) : base(key, name) { + Properties = JsonConvert.DeserializeObject(properties.ToString()); + CommFeedbacks = new Dictionary(); + CommDictionary = new Dictionary(); + Apis = new List(); + Key = key; + + } + + + + public override bool CustomActivate() + { + int commNumber = 1; + foreach (var commConfig in Properties.CommConnections) { + var commPort = new EssentialCommsPort(commConfig.control, string.Format("{0}-{1}", Key, commConfig.joinNumber)); + CommDictionary.Add(commConfig.joinNumber, commPort); + commNumber++; + } + + foreach (var Ipid in Properties.EiscApiIpids) { + var ApiEisc = new BridgeApiEisc(Ipid); + Apis.Add(ApiEisc); + foreach (var commConnection in CommDictionary) { + Debug.Console(0, "Joining Api{0} to comm {1}", Ipid, commConnection.Key); + var tempComm = commConnection.Value; + var tempJoin = (uint)commConnection.Key; + EssentialComApiMap ApiMap = new EssentialComApiMap(ApiEisc, (uint)tempJoin); + + tempComm.Outputs.Add(ApiMap); + // Check for ApiMap Overide Values here + + ApiEisc.Eisc.SetBoolSigAction(tempJoin, b => { + if (b) { tempComm.Comm.Connect(); } else { tempComm.Comm.Disconnect(); } + }); + ApiEisc.Eisc.SetStringSigAction(tempJoin, s => tempComm.Comm.SendText(s)); + + tempComm.StatusFeedback.LinkInputSig(ApiEisc.Eisc.UShortInput[tempJoin]); + tempComm.ConnectedFeedback.LinkInputSig(ApiEisc.Eisc.BooleanInput[tempJoin]); + + + + } + + } + + return true; + } + + + } + public class EssentialComApiMap { + public uint Join; + public BridgeApiEisc Api; + public uint connectJoin; + public EssentialComApiMap(BridgeApiEisc api, uint join) { + Join = join; + Api = api; + } + } + } \ No newline at end of file diff --git a/essentials-framework/Essentials/PepperDashEssentials/Bridges/EssentialDM.cs b/essentials-framework/Essentials/PepperDashEssentials/Bridges/EssentialDM.cs new file mode 100644 index 00000000..f6484ca1 --- /dev/null +++ b/essentials-framework/Essentials/PepperDashEssentials/Bridges/EssentialDM.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Core; +using PepperDash.Essentials.Core.Routing; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.EthernetCommunication; + +namespace PepperDash.Essentials { + public class EssentialDM : PepperDash.Core.Device { + public EssentialDMProperties Properties; + public List BridgeApiEiscs; + private PepperDash.Essentials.DM.DmChassisController DmSwitch; + private EssentialDMApiMap ApiMap = new EssentialDMApiMap(); + public EssentialDM(string key, string name, JToken properties) + : base(key, name) { + Properties = JsonConvert.DeserializeObject(properties.ToString()); + + + } + public override bool CustomActivate() { + // Create EiscApis + try { + foreach (var device in DeviceManager.AllDevices) { + if (device.Key == this.Properties.connectionDeviceKey) { + Debug.Console(0, "deviceKey {0} Matches", device.Key); + DmSwitch = DeviceManager.GetDeviceForKey(device.Key) as PepperDash.Essentials.DM.DmChassisController; + + } else { + Debug.Console(0, "deviceKey {0} doesn't match", device.Key); + } + } + if (Properties.EiscApiIpids != null) { + foreach (string Ipid in Properties.EiscApiIpids) { + var ApiEisc = new BridgeApiEisc(Ipid); + // BridgeApiEiscs.Add(ApiEisc); + //Essentials.Core.CommFactory.CreateCommForConfig(); + + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputVideoRoutes[1], u => DmSwitch.ExecuteSwitch(u, 1, eRoutingSignalType.Video)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputVideoRoutes[2], u => DmSwitch.ExecuteSwitch(u, 2, eRoutingSignalType.Video)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputVideoRoutes[3], u => DmSwitch.ExecuteSwitch(u, 3, eRoutingSignalType.Video)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputVideoRoutes[4], u => DmSwitch.ExecuteSwitch(u, 4, eRoutingSignalType.Video)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputVideoRoutes[5], u => DmSwitch.ExecuteSwitch(u, 5, eRoutingSignalType.Video)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputVideoRoutes[6], u => DmSwitch.ExecuteSwitch(u, 6, eRoutingSignalType.Video)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputVideoRoutes[7], u => DmSwitch.ExecuteSwitch(u, 7, eRoutingSignalType.Video)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputVideoRoutes[8], u => DmSwitch.ExecuteSwitch(u, 8, eRoutingSignalType.Video)); + + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputAudioRoutes[1], u => DmSwitch.ExecuteSwitch(u, 1, eRoutingSignalType.Audio)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputAudioRoutes[2], u => DmSwitch.ExecuteSwitch(u, 2, eRoutingSignalType.Audio)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputAudioRoutes[3], u => DmSwitch.ExecuteSwitch(u, 3, eRoutingSignalType.Audio)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputAudioRoutes[4], u => DmSwitch.ExecuteSwitch(u, 4, eRoutingSignalType.Audio)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputAudioRoutes[5], u => DmSwitch.ExecuteSwitch(u, 5, eRoutingSignalType.Audio)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputAudioRoutes[6], u => DmSwitch.ExecuteSwitch(u, 6, eRoutingSignalType.Audio)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputAudioRoutes[7], u => DmSwitch.ExecuteSwitch(u, 7, eRoutingSignalType.Audio)); + ApiEisc.Eisc.SetUShortSigAction(ApiMap.OutputAudioRoutes[8], u => DmSwitch.ExecuteSwitch(u, 8, eRoutingSignalType.Audio)); + + foreach (var output in DmSwitch.Chassis.Outputs) { + Debug.Console(0, "Creating EiscActions {0}", output.Number); + + DmSwitch.InputEndpointOnlineFeedbacks[(ushort)output.Number].LinkInputSig(ApiEisc.Eisc.BooleanInput[(ushort)(output.Number + 300)]); + + + /* This wont work...routes to 8 every time i tried for loops, forweach. For some reason the test number keeps going to max value of the loop + * always routing testNum to MaxLoopValue, the above works though.*/ + + for (uint testNum = 1; testNum < 8; testNum++) { + uint num = testNum; + ApiEisc.Eisc.SetUShortSigAction((ushort)(output.Number + 300), u => DmSwitch.ExecuteSwitch(u, num, eRoutingSignalType.Audio)); + ApiEisc.Eisc.SetUShortSigAction((ushort)(output.Number + 100), u => DmSwitch.ExecuteSwitch(u, num, eRoutingSignalType.Video)); + } + + DmSwitch.OutputRouteFeedbacks[(ushort)output.Number].LinkInputSig(ApiEisc.Eisc.UShortInput[ApiMap.OutputVideoRoutes[(int)output.Number]]); + } + DmSwitch.IsOnline.LinkInputSig(ApiEisc.Eisc.BooleanInput[ApiMap.ChassisOnline]); + ApiEisc.Eisc.Register(); + } + } + + + + Debug.Console(0, "Name {0} Activated", this.Name); + return true; + } catch (Exception e) { + Debug.Console(0, "BRidge {0}", e); + return false; + } + } + } + public class EssentialDMProperties { + public string connectionDeviceKey; + public string[] EiscApiIpids; + + + } + + + public class EssentialDMApiMap { + public ushort ChassisOnline = 11; + public Dictionary OutputVideoRoutes; + public Dictionary OutputAudioRoutes; + + public EssentialDMApiMap() { + OutputVideoRoutes = new Dictionary(); + OutputAudioRoutes = new Dictionary(); + + for (int x = 1; x <= 200; x++) { + // Debug.Console(0, "Init Value {0}", x); + OutputVideoRoutes[x] = (ushort)(x + 100); + OutputAudioRoutes[x] = (ushort)(x + 300); + } + } + } + } + \ No newline at end of file diff --git a/essentials-framework/Essentials/PepperDashEssentials/Configuration Original/Factories/DmFactory.cs b/essentials-framework/Essentials/PepperDashEssentials/Configuration Original/Factories/DmFactory.cs new file mode 100644 index 00000000..0a0d4370 --- /dev/null +++ b/essentials-framework/Essentials/PepperDashEssentials/Configuration Original/Factories/DmFactory.cs @@ -0,0 +1,96 @@ +using System; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints.Receivers; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; + +using Newtonsoft.Json.Linq; + +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices; +//using PepperDash.Essentials.Devices.Dm; +using PepperDash.Core; + +namespace PepperDash.Essentials +{ + public class DmFactory + { + public static Device Create(JToken devToken) + { + Device dev = null; + try + { + var devType = devToken.Value("type"); + var devKey = devToken.Value("key"); + var devName = devToken.Value("name"); + // Catch all 200 series TX + var devprops = devToken["properties"]; + var ipId = Convert.ToUInt32(devprops.Value("ipId"), 16); + var parent = devprops.Value("parent"); + if (parent == null) + parent = "controlSystem"; + + if (devType.StartsWith("DmTx2", StringComparison.OrdinalIgnoreCase)) + { + DmTx201C tx; + if (parent.Equals("controlSystem", StringComparison.OrdinalIgnoreCase)) + { + tx = new DmTx201C(ipId, Global.ControlSystem); + //dev = new DmTx201SBasicController(devKey, devName, tx); + } + + } + else if (devType.StartsWith("DmRmc", StringComparison.OrdinalIgnoreCase)) + { + DmRmc100C rmc; + if (parent.Equals("controlSystem", StringComparison.OrdinalIgnoreCase)) + { + rmc = new DmRmc100C(ipId, Global.ControlSystem); + //dev = new DmRmcBaseController(devKey, devName, rmc); + } + } + else + FactoryHelper.HandleUnknownType(devToken, devType); + } + catch (Exception e) + { + FactoryHelper.HandleDeviceCreationError(devToken, e); + } + return dev; + } + + + public static Device CreateChassis(JToken devToken) + { + Device dev = null; + try + { + var devType = devToken.Value("type"); + var devKey = devToken.Value("key"); + var devName = devToken.Value("name"); + // Catch all 200 series TX + var devprops = devToken["properties"]; + var ipId = Convert.ToUInt32(devprops.Value("ipId"), 16); + var parent = devprops.Value("parent"); + if (parent == null) + parent = "controlSystem"; + + if (devType.Equals("dmmd8x8", StringComparison.OrdinalIgnoreCase)) + { + var dm = new DmMd8x8(ipId, Global.ControlSystem); + //dev = new DmChassisController(devKey, devName, dm); + } + else if (devType.Equals("dmmd16x16", StringComparison.OrdinalIgnoreCase)) + { + var dm = new DmMd16x16(ipId, Global.ControlSystem); + //dev = new DmChassisController(devKey, devName, dm); + } + } + catch (Exception e) + { + FactoryHelper.HandleDeviceCreationError(devToken, e); + } + return dev; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials/PepperDashEssentials/ControlSystem.cs b/essentials-framework/Essentials/PepperDashEssentials/ControlSystem.cs new file mode 100644 index 00000000..bde618c0 --- /dev/null +++ b/essentials-framework/Essentials/PepperDashEssentials/ControlSystem.cs @@ -0,0 +1,309 @@ +using System; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.CrestronThread; +using PepperDash.Core; +// using PepperDash.Core.PortalSync; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common; +using PepperDash.Essentials.DM; +using PepperDash.Essentials.Fusion; +using PepperDash.Essentials.Room.Cotija; + +namespace PepperDash.Essentials +{ + public class ControlSystem : CrestronControlSystem + { + // PepperDashPortalSyncClient PortalSync; + HttpLogoServer LogoServer; + + public ControlSystem() + : base() + { + Thread.MaxNumberOfUserThreads = 400; + Global.ControlSystem = this; + DeviceManager.Initialize(this); + } + + /// + /// Git 'er goin' + /// + public override void InitializeSystem() + { + CrestronConsole.AddNewConsoleCommand(s => + { + foreach (var tl in TieLineCollection.Default) + CrestronConsole.ConsoleCommandResponse(" {0}\r", tl); + }, + "listtielines", "Prints out all tie lines", ConsoleAccessLevelEnum.AccessOperator); + + CrestronConsole.AddNewConsoleCommand(s => + { + CrestronConsole.ConsoleCommandResponse + ("Current running configuration. This is the merged system and template configuration"); + CrestronConsole.ConsoleCommandResponse(Newtonsoft.Json.JsonConvert.SerializeObject + (ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented)); + }, "showconfig", "Shows the current running merged config", ConsoleAccessLevelEnum.AccessOperator); + + CrestronConsole.AddNewConsoleCommand(s => + { + CrestronConsole.ConsoleCommandResponse("This system can be found at the following URLs:\r" + + "System URL: {0}\r" + + "Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl); + }, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator); + + GoWithLoad(); + } + + /// + /// Do it, yo + /// + public void GoWithLoad() + { + try + { + CrestronConsole.AddNewConsoleCommand(EnablePortalSync, "portalsync", "Loads Portal Sync", + ConsoleAccessLevelEnum.AccessOperator); + + //PortalSync = new PepperDashPortalSyncClient(); + + Debug.Console(0, "Starting Essentials load from configuration"); + + var filesReady = SetupFilesystem(); + if (filesReady) + { + Debug.Console(0, "Folder structure verified. Loading config..."); + if (!ConfigReader.LoadConfig2()) + return; + + Load(); + Debug.Console(0, "Essentials load complete\r" + + "-------------------------------------------------------------"); + } + else + { + Debug.Console(0, + "------------------------------------------------\r" + + "------------------------------------------------\r" + + "------------------------------------------------\r" + + "Essentials file structure setup completed.\r" + + "Please load config, sgd and ir files and\r" + + "restart program.\r" + + "------------------------------------------------\r" + + "------------------------------------------------\r" + + "------------------------------------------------"); + } + } + catch (Exception e) + { + Debug.Console(0, "FATAL INITIALIZE ERROR. System is in an inconsistent state:\r{0}", e); + } + + } + + /// + /// Verifies filesystem is set up. IR, SGD, and program1 folders + /// + bool SetupFilesystem() + { + Debug.Console(0, "Verifying and/or creating folder structure"); + var appNum = InitialParametersClass.ApplicationNumber; + var configDir = @"\NVRAM\Program" + appNum; + var configExists = Directory.Exists(configDir); + if (!configExists) + Directory.Create(configDir); + + var irDir = string.Format(@"\NVRAM\Program{0}\ir", appNum); + if (!Directory.Exists(irDir)) + Directory.Create(irDir); + + var sgdDir = string.Format(@"\NVRAM\Program{0}\sgd", appNum); + if (!Directory.Exists(sgdDir)) + Directory.Create(sgdDir); + + return configExists; + } + + public void EnablePortalSync(string s) + { + if (s.ToLower() == "enable") + { + CrestronConsole.ConsoleCommandResponse("Portal Sync features enabled"); + // PortalSync = new PepperDashPortalSyncClient(); + } + } + + public void TearDown() + { + Debug.Console(0, "Tearing down existing system"); + DeviceManager.DeactivateAll(); + + TieLineCollection.Default.Clear(); + + foreach (var key in DeviceManager.GetDevices()) + DeviceManager.RemoveDevice(key); + + Debug.Console(0, "Tear down COMPLETE"); + } + + /// + /// + /// + void Load() + { + LoadDevices(); + LoadTieLines(); + LoadRooms(); + LoadLogoServer(); + + DeviceManager.ActivateAll(); + } + + + /// + /// Reads all devices from config and adds them to DeviceManager + /// + public void LoadDevices() + { + foreach (var devConf in ConfigReader.ConfigObject.Devices) + { + + try + { + Debug.Console(0, "Creating device '{0}'", devConf.Key); + // Skip this to prevent unnecessary warnings + if (devConf.Key == "processor") + continue; + + // Try local factory first + var newDev = DeviceFactory.GetDevice(devConf); + + // Then associated library factories + if (newDev == null) + newDev = PepperDash.Essentials.Devices.Common.DeviceFactory.GetDevice(devConf); + if (newDev == null) + newDev = PepperDash.Essentials.DM.DeviceFactory.GetDevice(devConf); + if (newDev == null) + newDev = PepperDash.Essentials.Devices.Displays.DisplayDeviceFactory.GetDevice(devConf); + if (newDev == null) + newDev = PepperDash.Essentials.BridgeFactory.GetDevice(devConf); + + if (newDev != null) + DeviceManager.AddDevice(newDev); + else + Debug.Console(0, "ERROR: Cannot load unknown device type '{0}', key '{1}'.", devConf.Type, devConf.Key); + } + catch (Exception e) + { + Debug.Console(0, "ERROR: Creating device {0}. Skipping device. \r{1}", devConf.Key, e); + } + } + } + + /// + /// Helper method to load tie lines. This should run after devices have loaded + /// + public void LoadTieLines() + { + // In the future, we can't necessarily just clear here because devices + // might be making their own internal sources/tie lines + + var tlc = TieLineCollection.Default; + //tlc.Clear(); + if (ConfigReader.ConfigObject.TieLines == null) + { + return; + } + + foreach (var tieLineConfig in ConfigReader.ConfigObject.TieLines) + { + var newTL = tieLineConfig.GetTieLine(); + if (newTL != null) + tlc.Add(newTL); + } + } + + /// + /// Reads all rooms from config and adds them to DeviceManager + /// + public void LoadRooms() + { + if (ConfigReader.ConfigObject.Rooms == null) + { + Debug.Console(0, "WARNING: Configuration contains no rooms"); + return; + } + + foreach (var roomConfig in ConfigReader.ConfigObject.Rooms) + { + var room = roomConfig.GetRoomObject(); + if (room != null) + { + if (room is EssentialsHuddleSpaceRoom) + { + DeviceManager.AddDevice(room); + + Debug.Console(1, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion"); + DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemControllerBase((EssentialsHuddleSpaceRoom)room, 0xf1)); + + // Cotija bridge + var bridge = new CotijaEssentialsHuddleSpaceRoomBridge(room as EssentialsHuddleSpaceRoom); + AddBridgePostActivationHelper(bridge); // Lets things happen later when all devices are present + DeviceManager.AddDevice(bridge); + } + else if (room is EssentialsHuddleVtc1Room) + { + DeviceManager.AddDevice(room); + + Debug.Console(1, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion"); + DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, 0xf1)); + } + else + { + Debug.Console(1, "Room is NOT EssentialsHuddleSpaceRoom, attempting to add to DeviceManager w/o Fusion"); + DeviceManager.AddDevice(room); + } + + } + else + Debug.Console(0, "WARNING: Cannot create room from config, key '{0}'", roomConfig.Key); + } + } + + /// + /// Helps add the post activation steps that link bridges to main controller + /// + /// + void AddBridgePostActivationHelper(CotijaBridgeBase bridge) + { + bridge.AddPostActivationAction(() => + { + var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController; + if (parent == null) + { + Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present"); + } + Debug.Console(0, bridge, "Linking to parent controller"); + bridge.AddParent(parent); + parent.AddBridge(bridge); + }); + } + + /// + /// Fires up a logo server if not already running + /// + void LoadLogoServer() + { + try + { + LogoServer = new HttpLogoServer(8080, @"\html\logo"); + } + catch (Exception) + { + Debug.Console(0, "NOTICE: Logo server cannot be started. Likely already running in another program"); + } + } + } +} diff --git a/essentials-framework/Essentials/PepperDashEssentials/PepperDashEssentials.csproj b/essentials-framework/Essentials/PepperDashEssentials/PepperDashEssentials.csproj new file mode 100644 index 00000000..463928a4 --- /dev/null +++ b/essentials-framework/Essentials/PepperDashEssentials/PepperDashEssentials.csproj @@ -0,0 +1,224 @@ + + + Release + AnyCPU + 9.0.30729 + 2.0 + {1BED5BA9-88C4-4365-9362-6F4B128071D3} + Library + Properties + PepperDash.Essentials + PepperDashEssentials + {0B4745B0-194B-4BB6-8E21-E9057CA92230};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + WindowsCE + E2BECB1F-8C8C-41ba-B736-9BE7D946A398 + 5.0 + SmartDeviceProject1 + v3.5 + Windows CE + + + + + .allowedReferenceRelatedFileExtensions + true + full + false + bin\ + DEBUG;TRACE; + prompt + 4 + 512 + true + true + off + + + .allowedReferenceRelatedFileExtensions + none + true + bin\ + prompt + 4 + 512 + true + true + off + + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DeviceSupport.dll + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.DM.dll + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.EthernetCommunications.dll + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Fusion.dll + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Remotes.dll + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll + + + + False + ..\..\Release Package\PepperDash_Core.dll + + + False + ..\..\Essentials DM\Essentials_DM\bin\PepperDash_Essentials_DM.dll + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + + + False + ..\..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {A49AD6C8-FC0A-4CC0-9089-DFB4CF92D2B5} + PepperDash_Essentials_Core + + + {892B761C-E479-44CE-BD74-243E9214AF13} + Essentials Devices Common + + + + + + + + + rem S# Pro preparation will execute after these operations + + \ No newline at end of file diff --git a/essentials-framework/Essentials/PepperDashEssentials/UIDrivers/DualDisplayRouting REMOVE.cs b/essentials-framework/Essentials/PepperDashEssentials/UIDrivers/DualDisplayRouting REMOVE.cs new file mode 100644 index 00000000..1aeb7a3c --- /dev/null +++ b/essentials-framework/Essentials/PepperDashEssentials/UIDrivers/DualDisplayRouting REMOVE.cs @@ -0,0 +1,231 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; +//using Crestron.SimplSharpPro; +//using Crestron.SimplSharpPro.DeviceSupport; +//using PepperDash.Core; +//using PepperDash.Essentials.Core; +//using PepperDash.Essentials.Core.SmartObjects; +//using PepperDash.Essentials.Core.PageManagers; + +//namespace PepperDash.Essentials +//{ +// public class DualDisplaySimpleOrAdvancedRouting : PanelDriverBase +// { +// EssentialsPresentationPanelAvFunctionsDriver Parent; + +// /// +// /// Smart Object 3200 +// /// +// SubpageReferenceList SourcesSrl; + +// /// +// /// For tracking feedback on last selected +// /// +// BoolInputSig LastSelectedSourceSig; + +// /// +// /// The source that has been selected and is awaiting assignment to a display +// /// +// SourceListItem PendingSource; + +// bool IsSharingModeAdvanced; + +// public DualDisplaySimpleOrAdvancedRouting(EssentialsPresentationPanelAvFunctionsDriver parent) : base(parent.TriList) +// { +// Parent = parent; +// SourcesSrl = new SubpageReferenceList(TriList, 3200, 3, 3, 3); + +// TriList.SetSigFalseAction(UIBoolJoin.ToggleSharingModePress, ToggleSharingModePressed); + +// TriList.SetSigFalseAction(UIBoolJoin.Display1AudioButtonPressAndFb, Display1AudioPress); +// TriList.SetSigFalseAction(UIBoolJoin.Display1ControlButtonPress, Display1ControlPress); +// TriList.SetSigTrueAction(UIBoolJoin.Display1SelectPressAndFb, Display1Press); + +// TriList.SetSigFalseAction(UIBoolJoin.Display2AudioButtonPressAndFb, Display2AudioPress); +// TriList.SetSigFalseAction(UIBoolJoin.Display2ControlButtonPress, Display2ControlPress); +// TriList.SetSigTrueAction(UIBoolJoin.Display2SelectPressAndFb, Display2Press); +// } + +// /// +// /// +// /// +// public override void Show() +// { +// TriList.BooleanInput[UIBoolJoin.ToggleSharingModeVisible].BoolValue = true; +// TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = true; +// if(IsSharingModeAdvanced) +// TriList.BooleanInput[UIBoolJoin.DualDisplayPageVisible].BoolValue = true; +// else +// TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = true; +// base.Show(); +// } + +// /// +// /// +// /// +// //public override void Hide() +// //{ +// // TriList.BooleanInput[UIBoolJoin.ToggleSharingModeVisible].BoolValue = false; +// // TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = false; +// // if(IsSharingModeAdvanced) +// // TriList.BooleanInput[UIBoolJoin.DualDisplayPageVisible].BoolValue = false; +// // else +// // TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; +// // base.Hide(); +// //} + +// public void SetCurrentRoomFromParent() +// { +// if (IsSharingModeAdvanced) +// return; // add stuff here +// else +// SetupSourceListForSimpleRouting(); +// } + +// /// +// /// +// /// +// void SetupSourceListForSimpleRouting() +// { +// // get the source list config and set up the source list +// var config = ConfigReader.ConfigObject.SourceLists; +// if (config.ContainsKey(Parent.CurrentRoom.SourceListKey)) +// { +// var srcList = config[Parent.CurrentRoom.SourceListKey] +// .Values.ToList().OrderBy(s => s.Order); +// // Setup sources list +// uint i = 1; // counter for UI list +// foreach (var srcConfig in srcList) +// { +// if (!srcConfig.IncludeInSourceList) // Skip sources marked this way +// continue; + +// var sourceKey = srcConfig.SourceKey; +// var actualSource = DeviceManager.GetDeviceForKey(sourceKey) as Device; +// if (actualSource == null) +// { +// Debug.Console(0, "Cannot assign missing source '{0}' to source UI list", +// srcConfig.SourceKey); +// continue; +// } +// var localSrcItem = srcConfig; // lambda scope below +// var localIndex = i; +// SourcesSrl.GetBoolFeedbackSig(i, 1).UserObject = new Action(b => +// { +// if (IsSharingModeAdvanced) +// { +// if (LastSelectedSourceSig != null) +// LastSelectedSourceSig.BoolValue = false; +// SourceListButtonPress(localSrcItem); +// LastSelectedSourceSig = SourcesSrl.BoolInputSig(localIndex, 1); +// LastSelectedSourceSig.BoolValue = true; +// } +// else +// Parent.CurrentRoom.DoSourceToAllDestinationsRoute(localSrcItem); +// }); +// SourcesSrl.StringInputSig(i, 1).StringValue = srcConfig.PreferredName; +// i++; + +// //var item = new SubpageReferenceListSourceItem(i++, SourcesSrl, srcConfig, +// // b => { if (!b) UiSelectSource(localSrcConfig); }); +// //SourcesSrl.AddItem(item); // add to the SRL +// //item.RegisterForSourceChange(Parent.CurrentRoom); +// } +// SourcesSrl.Count = (ushort)(i - 1); +// Parent.CurrentRoom.CurrentSingleSourceChange += CurrentRoom_CurrentSourceInfoChange; +// Parent.CurrentRoom.CurrentDisplay1SourceChange += CurrentRoom_CurrentDisplay1SourceChange; +// Parent.CurrentRoom.CurrentDisplay2SourceChange += CurrentRoom_CurrentDisplay2SourceChange; +// } +// } + +// void SetupSourceListForAdvancedRouting() +// { + +// } + +// void CurrentRoom_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) +// { + +// } + +// void CurrentRoom_CurrentDisplay1SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) +// { +// TriList.StringInput[UIStringJoin.Display1SourceLabel].StringValue = PendingSource.PreferredName; + +// } + +// void CurrentRoom_CurrentDisplay2SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) +// { +// TriList.StringInput[UIStringJoin.Display2SourceLabel].StringValue = PendingSource.PreferredName; +// } + +// /// +// /// +// /// +// void ToggleSharingModePressed() +// { +// Hide(); +// IsSharingModeAdvanced = !IsSharingModeAdvanced; +// TriList.BooleanInput[UIBoolJoin.ToggleSharingModePress].BoolValue = IsSharingModeAdvanced; +// Show(); +// } + +// public void SourceListButtonPress(SourceListItem item) +// { +// // start the timer +// // show FB on potential source +// TriList.BooleanInput[UIBoolJoin.Display1AudioButtonEnable].BoolValue = false; +// TriList.BooleanInput[UIBoolJoin.Display1ControlButtonEnable].BoolValue = false; +// TriList.BooleanInput[UIBoolJoin.Display2AudioButtonEnable].BoolValue = false; +// TriList.BooleanInput[UIBoolJoin.Display2ControlButtonEnable].BoolValue = false; +// PendingSource = item; +// } + +// void EnableAppropriateDisplayButtons() +// { +// TriList.BooleanInput[UIBoolJoin.Display1AudioButtonEnable].BoolValue = true; +// TriList.BooleanInput[UIBoolJoin.Display1ControlButtonEnable].BoolValue = true; +// TriList.BooleanInput[UIBoolJoin.Display2AudioButtonEnable].BoolValue = true; +// TriList.BooleanInput[UIBoolJoin.Display2ControlButtonEnable].BoolValue = true; +// if (LastSelectedSourceSig != null) +// LastSelectedSourceSig.BoolValue = false; +// } + +// public void Display1Press() +// { +// EnableAppropriateDisplayButtons(); +// Parent.CurrentRoom.SourceToDisplay1(PendingSource); +// // Enable end meeting +// } + +// public void Display1AudioPress() +// { + +// } + + +// public void Display1ControlPress() +// { + +// } + +// public void Display2Press() +// { +// EnableAppropriateDisplayButtons(); +// Parent.CurrentRoom.SourceToDisplay2(PendingSource); +// } + +// public void Display2AudioPress() +// { + +// } + +// public void Display2ControlPress() +// { + +// } +// } +//} \ No newline at end of file diff --git a/essentials-framework/references/PepperDash_Core.dll b/essentials-framework/references/PepperDash_Core.dll new file mode 100644 index 00000000..428b9249 Binary files /dev/null and b/essentials-framework/references/PepperDash_Core.dll differ