using System; using System.Collections.Generic; using System.Linq; using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DM; using Crestron.SimplSharpPro.DM.Cards; using Crestron.SimplSharpPro.DM.Endpoints.Receivers; using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.DeviceInfo; using PepperDash.Essentials.DM.Config; using PepperDash.Essentials.Core.Config; using PepperDash_Essentials_DM; namespace PepperDash.Essentials.DM { [Description("Wrapper class for all DM-RMC variants")] public abstract class DmRmcControllerBase : CrestronGenericBridgeableBaseDevice, IDeviceInfoProvider { private const int CtpPort = 41795; private readonly EndpointReceiverBase _rmc; //kept here just in case. Only property or method on this class that's not device-specific is the DMOutput that it's attached to. public StringFeedback VideoOutputResolutionFeedback { get; protected set; } public StringFeedback EdidManufacturerFeedback { get; protected set; } public StringFeedback EdidNameFeedback { get; protected set; } public StringFeedback EdidPreferredTimingFeedback { get; protected set; } public StringFeedback EdidSerialNumberFeedback { get; protected set; } protected DmRmcControllerBase(string key, string name, EndpointReceiverBase device) : base(key, name, device) { _rmc = device; // if wired to a chassis, skip registration step in base class PreventRegistration = _rmc.DMOutput != null; AddToFeedbackList(VideoOutputResolutionFeedback, EdidManufacturerFeedback, EdidSerialNumberFeedback, EdidNameFeedback, EdidPreferredTimingFeedback); DeviceInfo = new DeviceInfo(); IsOnline.OutputChange += (currentDevice, args) => { if (args.BoolValue) UpdateDeviceInfo(); }; } protected void LinkDmRmcToApi(DmRmcControllerBase rmc, BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) { var joinMap = new DmRmcControllerJoinMap(joinStart); var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); if (!string.IsNullOrEmpty(joinMapSerialized)) joinMap = JsonConvert.DeserializeObject(joinMapSerialized); if (bridge != null) { bridge.AddJoinMap(Key, joinMap); } else { Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); } Debug.Console(1, rmc, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); trilist.StringInput[joinMap.Name.JoinNumber].StringValue = rmc.Name; if (rmc.VideoOutputResolutionFeedback != null) rmc.VideoOutputResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentOutputResolution.JoinNumber]); if (rmc.EdidManufacturerFeedback != null) rmc.EdidManufacturerFeedback.LinkInputSig(trilist.StringInput[joinMap.EdidManufacturer.JoinNumber]); if (rmc.EdidNameFeedback != null) rmc.EdidNameFeedback.LinkInputSig(trilist.StringInput[joinMap.EdidName.JoinNumber]); if (rmc.EdidPreferredTimingFeedback != null) rmc.EdidPreferredTimingFeedback.LinkInputSig(trilist.StringInput[joinMap.EdidPrefferedTiming.JoinNumber]); if (rmc.EdidSerialNumberFeedback != null) rmc.EdidSerialNumberFeedback.LinkInputSig(trilist.StringInput[joinMap.EdidSerialNumber.JoinNumber]); //If the device is an DM-RMC-4K-Z-SCALER-C var routing = rmc as IRoutingInputsOutputs; trilist.UShortInput[joinMap.HdcpInputPortCount.JoinNumber].UShortValue = (ushort)(routing == null ? 1 : routing.InputPorts.Count); if (routing == null) { return; } var hdcpCapability = eHdcpCapabilityType.HdcpSupportOff; if (routing.InputPorts[DmPortName.HdmiIn] != null) { var hdmiInHdcp = routing as IHasHdmiInHdcp; if (hdmiInHdcp != null) { if (rmc.Feedbacks["HdmiInHdcpCapability"] != null) { var intFeedback = rmc.Feedbacks["HdmiInHdcpCapability"] as IntFeedback; if (intFeedback != null) intFeedback.LinkInputSig(trilist.UShortInput[joinMap.Port1HdcpState.JoinNumber]); } if (rmc.Feedbacks["HdmiInVideoSync"] != null) { var boolFeedback = rmc.Feedbacks["HdmiInVideoSync"] as BoolFeedback; if (boolFeedback != null) boolFeedback.LinkInputSig(trilist.BooleanInput[joinMap.HdmiInputSync.JoinNumber]); } hdcpCapability = hdmiInHdcp.HdmiInHdcpCapability > hdcpCapability ? hdmiInHdcp.HdmiInHdcpCapability : hdcpCapability; trilist.SetUShortSigAction(joinMap.Port1HdcpState.JoinNumber, a => hdmiInHdcp.SetHdmiInHdcpState((eHdcpCapabilityType)a)); } } if (routing.InputPorts[DmPortName.DmIn] != null) { var dmInHdcp = rmc as IHasDmInHdcp; if (dmInHdcp != null) { if (rmc.Feedbacks["DmInHdcpCapability"] != null) { var intFeedback = rmc.Feedbacks["DmInHdcpCapability"] as IntFeedback; if (intFeedback != null) intFeedback.LinkInputSig(trilist.UShortInput[joinMap.Port2HdcpState.JoinNumber]); } hdcpCapability = dmInHdcp.DmInHdcpCapability > hdcpCapability ? dmInHdcp.DmInHdcpCapability : hdcpCapability; trilist.SetUShortSigAction(joinMap.Port2HdcpState.JoinNumber, a => dmInHdcp.SetDmInHdcpState((eHdcpCapabilityType)a)); } } trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber].UShortValue = (ushort)hdcpCapability; trilist.UShortInput[joinMap.HdcpInputPortCount.JoinNumber].UShortValue = (ushort)routing.InputPorts.Count; var routingWithFeedback = routing as IRmcRouting; if (routingWithFeedback == null) return; if (routingWithFeedback.AudioVideoSourceNumericFeedback != null) routingWithFeedback.AudioVideoSourceNumericFeedback.LinkInputSig( trilist.UShortInput[joinMap.AudioVideoSource.JoinNumber]); trilist.SetUShortSigAction(joinMap.AudioVideoSource.JoinNumber, a => routingWithFeedback.ExecuteNumericSwitch(a, 1, eRoutingSignalType.AudioVideo)); } #region Implementation of IDeviceInfoProvider public DeviceInfo DeviceInfo { get; private set; } public event DeviceInfoChangeHandler DeviceInfoChanged; public void UpdateDeviceInfo() { Debug.Console(1, this, "Updating Device Info"); if (_rmc.ConnectedIpList.Count == 0) { Debug.Console(1, this, "IP Address information not yet received. No device is online"); return; } DeviceInfo.IpAddress = _rmc.ConnectedIpList[0].DeviceIpAddress; foreach (var ip in _rmc.ConnectedIpList) { Debug.Console(0, this, "Connected IP Address: {0}", ip.DeviceIpAddress); } GetFirmwareAndSerialInfo(); OnDeviceInfoChange(); } private void GetFirmwareAndSerialInfo() { var tcpClient = new GenericTcpIpClient(String.Format("{0}-devInfoSocket", Key), _rmc.ConnectedIpList[0].DeviceIpAddress, CtpPort, 1024) { AutoReconnect = false, }; var gather = new CommunicationGather(tcpClient, "\r\n\r\n"); tcpClient.ConnectionChange += (sender, args) => { if (!args.Client.IsConnected) { OnDeviceInfoChange(); return; } args.Client.SendText("ver\r\n"); }; gather.LineReceived += (sender, args) => { //ignore console prompt if (args.Text.ToLower().Contains(">")) { return; } if (args.Text.ToLower().Contains("host")) { DeviceInfo.HostName = args.Text.Split(':')[1].Trim(); tcpClient.SendText("maca\r\n"); return; } if (args.Text.ToLower().Contains("mac")) { DeviceInfo.MacAddress = args.Text.Split(':')[1].Trim().Replace(" ", ":"); tcpClient.Disconnect(); return; } if (!args.Text.ToLower().Contains("rmc")) { return; } DeviceInfo.SerialNumber = args.Text.Split('[')[1].Split(' ')[4].Replace("#", ""); DeviceInfo.FirmwareVersion = args.Text.Split('[')[1].Split(' ')[1]; tcpClient.SendText("host\r\n"); }; tcpClient.Connect(); } private void OnDeviceInfoChange() { var handler = DeviceInfoChanged; if (handler == null) return; handler(this, new DeviceInfoEventArgs(DeviceInfo)); } #endregion } public abstract class DmHdBaseTControllerBase : CrestronGenericBridgeableBaseDevice { protected HDBaseTBase Rmc; /// /// Make a Crestron RMC and put it in here /// protected DmHdBaseTControllerBase(string key, string name, HDBaseTBase rmc) : base(key, name, rmc) { Rmc = rmc; } } public class DmRmcHelper { private static readonly Dictionary> ProcessorFactoryDict; private static readonly Dictionary> ChassisCpu3Dict; private static readonly Dictionary> ChassisDict; static DmRmcHelper() { ProcessorFactoryDict = new Dictionary> { {"dmrmc100c", (k, n, i) => new DmRmcX100CController(k, n, new DmRmc100C(i, Global.ControlSystem))}, {"dmrmc100s", (k, n, i) => new DmRmc100SController(k, n, new DmRmc100S(i, Global.ControlSystem))}, {"dmrmc4k100c", (k, n, i) => new DmRmcX100CController(k, n, new DmRmc4k100C(i, Global.ControlSystem))}, {"dmrmc4kz100c", (k, n, i) => new DmRmc4kZ100CController(k, n, new DmRmc4kz100C(i, Global.ControlSystem))}, {"dmrmc150s", (k, n, i) => new DmRmc150SController(k, n, new DmRmc150S(i, Global.ControlSystem))}, {"dmrmc200c", (k, n, i) => new DmRmc200CController(k, n, new DmRmc200C(i, Global.ControlSystem))}, {"dmrmc200s", (k, n, i) => new DmRmc200SController(k, n, new DmRmc200S(i, Global.ControlSystem))}, {"dmrmc200s2", (k, n, i) => new DmRmc200S2Controller(k, n, new DmRmc200S2(i, Global.ControlSystem))}, {"dmrmcscalerc", (k, n, i) => new DmRmcScalerCController(k, n, new DmRmcScalerC(i, Global.ControlSystem))}, {"dmrmcscalers", (k, n, i) => new DmRmcScalerSController(k, n, new DmRmcScalerS(i, Global.ControlSystem))}, { "dmrmcscalers2", (k, n, i) => new DmRmcScalerS2Controller(k, n, new DmRmcScalerS2(i, Global.ControlSystem)) }, { "dmrmc4kscalerc", (k, n, i) => new DmRmc4kScalerCController(k, n, new DmRmc4kScalerC(i, Global.ControlSystem)) }, { "dmrmc4kscalercdsp", (k, n, i) => new DmRmc4kScalerCDspController(k, n, new DmRmc4kScalerCDsp(i, Global.ControlSystem)) }, { "dmrmc4kzscalerc", (k, n, i) => new DmRmc4kZScalerCController(k, n, new DmRmc4kzScalerC(i, Global.ControlSystem)) } }; ChassisCpu3Dict = new Dictionary> { {"dmrmc100c", (k, n, d) => new DmRmcX100CController(k, n, new DmRmc100C(d))}, {"dmrmc100s", (k, n, d) => new DmRmc100SController(k, n, new DmRmc100S(d))}, {"dmrmc4k100c", (k, n, d) => new DmRmcX100CController(k, n, new DmRmc4k100C(d))}, {"dmrmc4kz100c", (k, n, d) => new DmRmc4kZ100CController(k, n, new DmRmc4kz100C(d))}, {"dmrmc150s", (k, n, d) => new DmRmc150SController(k, n, new DmRmc150S(d))}, {"dmrmc200c", (k, n, d) => new DmRmc200CController(k, n, new DmRmc200C(d))}, {"dmrmc200s", (k, n, d) => new DmRmc200SController(k, n, new DmRmc200S(d))}, {"dmrmc200s2", (k, n, d) => new DmRmc200S2Controller(k, n, new DmRmc200S2(d))}, {"dmrmcscalerc", (k, n, d) => new DmRmcScalerCController(k, n, new DmRmcScalerC(d))}, {"dmrmcscalers", (k, n, d) => new DmRmcScalerSController(k, n, new DmRmcScalerS(d))}, { "dmrmcscalers2", (k, n, d) => new DmRmcScalerS2Controller(k, n, new DmRmcScalerS2(d)) }, { "dmrmc4kscalerc", (k, n, d) => new DmRmc4kScalerCController(k, n, new DmRmc4kScalerC(d)) }, { "dmrmc4kscalercdsp", (k, n, d) => new DmRmc4kScalerCDspController(k, n, new DmRmc4kScalerCDsp(d)) }, { "dmrmc4kzscalerc", (k, n, d) => new DmRmc4kZScalerCController(k, n, new DmRmc4kzScalerC(d)) }, {"hdbasetrx", (k,n,d) => new HDBaseTRxController(k,n, new HDRx3CB(d))}, {"dmrmc4k100c1g", (k,n,d) => new DmRmc4k100C1GController(k,n, new DmRmc4K100C1G(d))} }; ChassisDict = new Dictionary> { {"dmrmc100c", (k, n, i, d) => new DmRmcX100CController(k, n, new DmRmc100C(i,d))}, {"dmrmc100s", (k, n,i, d) => new DmRmc100SController(k, n, new DmRmc100S(i,d))}, {"dmrmc4k100c", (k, n,i, d) => new DmRmcX100CController(k, n, new DmRmc4k100C(i,d))}, {"dmrmc4kz100c", (k, n,i, d) => new DmRmc4kZ100CController(k, n, new DmRmc4kz100C(i,d))}, {"dmrmc150s", (k, n,i, d) => new DmRmc150SController(k, n, new DmRmc150S(i,d))}, {"dmrmc200c", (k, n,i, d) => new DmRmc200CController(k, n, new DmRmc200C(i,d))}, {"dmrmc200s", (k, n,i, d) => new DmRmc200SController(k, n, new DmRmc200S(i,d))}, {"dmrmc200s2", (k, n,i, d) => new DmRmc200S2Controller(k, n, new DmRmc200S2(i,d))}, {"dmrmcscalerc", (k, n,i, d) => new DmRmcScalerCController(k, n, new DmRmcScalerC(i,d))}, {"dmrmcscalers", (k, n,i, d) => new DmRmcScalerSController(k, n, new DmRmcScalerS(i,d))}, { "dmrmcscalers2", (k, n,i, d) => new DmRmcScalerS2Controller(k, n, new DmRmcScalerS2(i, d)) }, { "dmrmc4kscalerc", (k, n,i, d) => new DmRmc4kScalerCController(k, n, new DmRmc4kScalerC(i, d)) }, { "dmrmc4kscalercdsp", (k, n,i, d) => new DmRmc4kScalerCDspController(k, n, new DmRmc4kScalerCDsp(i, d)) }, { "dmrmc4kzscalerc", (k, n,i, d) => new DmRmc4kZScalerCController(k, n, new DmRmc4kzScalerC(i, d)) }, {"hdbasetrx", (k,n,i,d) => new HDBaseTRxController(k,n, new HDRx3CB(i, d))}, {"dmrmc4k100c1g", (k,n,i,d) => new DmRmc4k100C1GController(k,n, new DmRmc4K100C1G(i, d))} }; } /// /// A factory method for various DmRmcControllers /// /// device key. Used to uniquely identify device /// device name /// device type name. Used to retrived the correct device /// Config from config file /// public static CrestronGenericBaseDevice GetDmRmcController(string key, string name, string typeName, DmRmcPropertiesConfig props) { typeName = typeName.ToLower(); var ipid = props.Control.IpIdInt; var pKey = props.ParentDeviceKey.ToLower(); // Non-DM-chassis endpoints return pKey == "processor" ? GetDmRmcControllerForProcessor(key, name, typeName, ipid) : GetDmRmcControllerForChassis(key, name, typeName, props, pKey, ipid); } private static CrestronGenericBaseDevice GetDmRmcControllerForChassis(string key, string name, string typeName, DmRmcPropertiesConfig props, string pKey, uint ipid) { var parentDev = DeviceManager.GetDeviceForKey(pKey); CrestronGenericBaseDevice rx; bool useChassisForOfflineFeedback = false; if (parentDev is DmpsRoutingController) { var dmps = parentDev as DmpsRoutingController; //Check that the input is within range of this chassis' possible inputs var num = props.ParentOutputNumber; Debug.Console(1, "Creating DMPS device '{0}'. Output number '{1}'.", key, num); if (num <= 0 || num > dmps.Dmps.SwitcherOutputs.Count) { Debug.Console(0, "Cannot create DMPS device '{0}'. Output number '{1}' is out of range", key, num); return null; } // Must use different constructor for DMPS4K types. No IPID if (Global.ControlSystemIsDmps4kType) { rx = GetDmRmcControllerForDmps4k(key, name, typeName, dmps, props.ParentOutputNumber); useChassisForOfflineFeedback = true; } else { rx = GetDmRmcControllerForDmps(key, name, typeName, ipid, dmps, props.ParentOutputNumber); if (typeName == "hdbasetrx" || typeName == "dmrmc4k100c1g") { useChassisForOfflineFeedback = true; } } if (useChassisForOfflineFeedback) { Debug.Console(0, "DM endpoint output {0} does not have direct online feedback, changing online feedback to chassis", num); rx.IsOnline.SetValueFunc(() => dmps.OutputEndpointOnlineFeedbacks[num].BoolValue); dmps.OutputEndpointOnlineFeedbacks[num].OutputChange += (o, a) => { foreach (var feedback in rx.Feedbacks) { if (feedback != null) feedback.FireUpdate(); } }; } return rx; } else if (parentDev is IDmSwitchWithEndpointOnlineFeedback) { var controller = parentDev as IDmSwitchWithEndpointOnlineFeedback; var chassis = controller.Chassis; var num = props.ParentOutputNumber; Debug.Console(1, "Creating DM Chassis device '{0}'. Output number '{1}'.", key, num); 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; } 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 || chassis is DmMd128x128 || chassis is DmMd64x64) { rx = GetDmRmcControllerForCpu3Chassis(key, name, typeName, chassis, num, parentDev); useChassisForOfflineFeedback = true; } else { rx = GetDmRmcControllerForCpu2Chassis(key, name, typeName, ipid, chassis, num, parentDev); if (typeName == "hdbasetrx" || typeName == "dmrmc4k100c1g") { useChassisForOfflineFeedback = true; } } if (useChassisForOfflineFeedback) { Debug.Console(0, "DM endpoint output {0} does not have direct online feedback, changing online feedback to chassis", num); rx.IsOnline.SetValueFunc(() => controller.OutputEndpointOnlineFeedbacks[num].BoolValue); controller.OutputEndpointOnlineFeedbacks[num].OutputChange += (o, a) => { foreach (var feedback in rx.Feedbacks) { if (feedback != null) feedback.FireUpdate(); } }; } return rx; } catch (Exception e) { Debug.Console(0, "[{0}] WARNING: Cannot create DM-RMC device: {1}", key, e.Message); return null; } } else { Debug.Console(0, "Cannot create DM device '{0}'. '{1}' is not a DM Chassis or DMPS.", key, pKey); return null; } } private static CrestronGenericBaseDevice GetDmRmcControllerForCpu2Chassis(string key, string name, string typeName, uint ipid, Switch chassis, uint num, IKeyed parentDev) { Func handler; if (ChassisDict.TryGetValue(typeName.ToLower(), out handler)) { return handler(key, name, ipid, chassis.Outputs[num]); } Debug.Console(0, "Cannot create DM-RMC of type '{0}' with parent device {1}", typeName, parentDev.Key); return null; } private static CrestronGenericBaseDevice GetDmRmcControllerForCpu3Chassis(string key, string name, string typeName, Switch chassis, uint num, IKeyed parentDev) { Func cpu3Handler; if (ChassisCpu3Dict.TryGetValue(typeName.ToLower(), out cpu3Handler)) { return cpu3Handler(key, name, chassis.Outputs[num]); } Debug.Console(0, "Cannot create DM-RMC of type '{0}' with parent device {1}", typeName, parentDev.Key); return null; } private static CrestronGenericBaseDevice GetDmRmcControllerForDmps(string key, string name, string typeName, uint ipid, DmpsRoutingController controller, uint num) { Func dmpsHandler; if (ChassisDict.TryGetValue(typeName.ToLower(), out dmpsHandler)) { var output = controller.Dmps.SwitcherOutputs[num] as DMOutput; if (output != null) { return dmpsHandler(key, name, ipid, output); } Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot attach DM-RMC of type '{0}' to output {1} on DMPS chassis. Output is not a DM Output.", typeName, num); return null; } Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot create DM-RMC of type '{0}' to output {1} on DMPS chassis", typeName, num); return null; } private static CrestronGenericBaseDevice GetDmRmcControllerForDmps4k(string key, string name, string typeName, DmpsRoutingController controller, uint num) { Func dmps4kHandler; if (ChassisCpu3Dict.TryGetValue(typeName.ToLower(), out dmps4kHandler)) { var output = controller.Dmps.SwitcherOutputs[num] as DMOutput; if (output != null) { return dmps4kHandler(key, name, output); } Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot attach DM-RMC of type '{0}' to output {1} on DMPS-4K chassis. Output is not a DM Output.", typeName, num); return null; } Debug.Console(0, Debug.ErrorLogLevel.Error, "Cannot create DM-RMC of type '{0}' to output {1} on DMPS-4K chassis", typeName, num); return null; } private static CrestronGenericBaseDevice GetDmRmcControllerForProcessor(string key, string name, string typeName, uint ipid) { try { Func handler; if (ProcessorFactoryDict.TryGetValue(typeName.ToLower(), out handler)) { return handler(key, name, ipid); } Debug.Console(0, "Cannot create DM-RMC of type: '{0}'", typeName); return null; } catch (Exception e) { Debug.Console(0, "[{0}] WARNING: Cannot create DM-RMC device: {1}", key, e.Message); return null; } } } public class DmRmcControllerFactory : EssentialsDeviceFactory { public DmRmcControllerFactory() { TypeNames = new List { "hdbasetrx", "dmrmc4k100c1g", "dmrmc100c", "dmrmc100s", "dmrmc4k100c", "dmrmc150s", "dmrmc200c", "dmrmc200s", "dmrmc200s2", "dmrmcscalerc", "dmrmcscalers", "dmrmcscalers2", "dmrmc4kscalerc", "dmrmc4kscalercdsp", "dmrmc4kz100c", "dmrmc4kzscalerc" }; } public override EssentialsDevice BuildDevice(DeviceConfig dc) { var type = dc.Type.ToLower(); Debug.Console(1, "Factory Attempting to create new DM-RMC Device"); var props = JsonConvert.DeserializeObject (dc.Properties.ToString()); return DmRmcHelper.GetDmRmcController(dc.Key, dc.Name, type, props); } } }