diff --git a/.github/workflows/add-issues-to-project.yml b/.github/workflows/add-issues-to-project.yml new file mode 100644 index 00000000..8811c0cc --- /dev/null +++ b/.github/workflows/add-issues-to-project.yml @@ -0,0 +1,37 @@ +name: Add bugs to bugs project + +on: + issues: + types: + - opened + - labeled + +jobs: + check-secret: + runs-on: ubuntu-latest + outputs: + my-key: ${{ steps.my-key.outputs.defined }} + steps: + - id: my-key + if: "${{ env.MY_KEY != '' }}" + run: echo "::set-output name=defined::true" + env: + MY_KEY: ${{ secrets.PROJECT_URL }} + throw-error: + name: Check + runs-on: ubuntu-latest + needs: [check-secret] + if: needs.check-secret.outputs.my-key != 'true' + steps: + - run: echo "The Project URL Repo Secret is empty" + add-to-project: + name: Add issue to project + runs-on: ubuntu-latest + needs: [check-secret] + if: needs.check-secret.outputs.my-key == 'true' + steps: + - uses: actions/add-to-project@main + with: + project-url: ${{ secrets.PROJECT_URL }} + github-token: ${{ secrets.GH_PROJECTS_PASSWORD }} + diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 736a250f..e40cce4e 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -80,7 +80,7 @@ namespace PepperDash.Essentials CrestronConsole.AddNewConsoleCommand(PluginLoader.ReportAssemblyVersions, "reportversions", "Reports the versions of the loaded assemblies", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(PepperDash.Essentials.Core.DeviceFactory.GetDeviceFactoryTypes, "gettypes", "Gets the device types that can be built. Accepts a filter string.", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(Core.DeviceFactory.GetDeviceFactoryTypes, "gettypes", "Gets the device types that can be built. Accepts a filter string.", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(BridgeHelper.PrintJoinMap, "getjoinmap", "map(s) for bridge or device on bridge [brKey [devKey]]", ConsoleAccessLevelEnum.AccessOperator); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/IAnalogInputJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/IAnalogInputJoinMap.cs new file mode 100644 index 00000000..eaf70f3a --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/IAnalogInputJoinMap.cs @@ -0,0 +1,34 @@ +using System; + +namespace PepperDash.Essentials.Core.Bridges +{ + public class IAnalogInputJoinMap : JoinMapBaseAdvanced + { + + [JoinName("InputValue")] + public JoinDataComplete InputValue = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, + new JoinMetadata { Description = "Input Value", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Analog }); + [JoinName("MinimumChange")] + public JoinDataComplete MinimumChange = new JoinDataComplete(new JoinData { JoinNumber = 2, JoinSpan = 1 }, + new JoinMetadata { Description = "Minimum voltage change required to reflect a change", JoinCapabilities = eJoinCapabilities.ToFromSIMPL, JoinType = eJoinType.Analog }); + + /// + /// Constructor to use when instantiating this Join Map without inheriting from it + /// + /// Join this join map will start at + public IAnalogInputJoinMap(uint joinStart) + : this(joinStart, typeof(IAnalogInputJoinMap)) + { + } + + /// + /// Constructor to use when extending this Join map + /// + /// Join this join map will start at + /// Type of the child join map + protected IAnalogInputJoinMap(uint joinStart, Type type) + : base(joinStart, type) + { + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/IDigitalInputJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/IDigitalInputJoinMap.cs index 82c09e54..83e6cdab 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/IDigitalInputJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/IDigitalInputJoinMap.cs @@ -7,7 +7,7 @@ namespace PepperDash.Essentials.Core.Bridges [JoinName("InputState")] public JoinDataComplete InputState = new JoinDataComplete(new JoinData { JoinNumber = 1, JoinSpan = 1 }, - new JoinMetadata { Description = "Room Email Url", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital }); + new JoinMetadata { Description = "Input State", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Digital }); /// /// Constructor to use when instantiating this Join Map without inheriting from it diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/IOPortConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/IOPortConfig.cs index 09061ff2..5fbe10e1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/IOPortConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/IOPortConfig.cs @@ -15,5 +15,7 @@ namespace PepperDash.Essentials.Core.CrestronIO public uint PortNumber { get; set; } [JsonProperty("disablePullUpResistor")] public bool DisablePullUpResistor { get; set; } + [JsonProperty("minimumChange")] + public int MinimumChange { get; set; } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/GenericVersiportAnalogInputDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/GenericVersiportAnalogInputDevice.cs new file mode 100644 index 00000000..70be2f6f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/GenericVersiportAnalogInputDevice.cs @@ -0,0 +1,208 @@ +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.Config; +using PepperDash.Essentials.Core.Bridges; + + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Core.CrestronIO +{ + /// + /// Represents a generic digital input deviced tied to a versiport + /// + public class GenericVersiportAnalogInputDevice : EssentialsBridgeableDevice, IAnalogInput + { + public Versiport InputPort { get; private set; } + + public IntFeedback InputValueFeedback { get; private set; } + public IntFeedback InputMinimumChangeFeedback { get; private set; } + + Func InputValueFeedbackFunc + { + get + { + return () => InputPort.AnalogIn; + } + } + + Func InputMinimumChangeFeedbackFunc + { + get { return () => InputPort.AnalogMinChange; } + } + + public GenericVersiportAnalogInputDevice(string key, string name, Func postActivationFunc, IOPortConfig config) : + base(key, name) + { + InputValueFeedback = new IntFeedback(InputValueFeedbackFunc); + InputMinimumChangeFeedback = new IntFeedback(InputMinimumChangeFeedbackFunc); + + AddPostActivationAction(() => + { + InputPort = postActivationFunc(config); + + InputPort.Register(); + + InputPort.SetVersiportConfiguration(eVersiportConfiguration.AnalogInput); + InputPort.AnalogMinChange = (ushort)(config.MinimumChange > 0 ? config.MinimumChange : 655); + if (config.DisablePullUpResistor) + InputPort.DisablePullUpResistor = true; + + InputPort.VersiportChange += InputPort_VersiportChange; + + Debug.Console(1, this, "Created GenericVersiportAnalogInputDevice on port '{0}'. DisablePullUpResistor: '{1}'", config.PortNumber, InputPort.DisablePullUpResistor); + + }); + + } + + /// + /// Set minimum voltage change for device to update voltage changed method + /// + /// valid values range from 0 - 65535, representing the full 100% range of the processor voltage source. Check processor documentation for details + public void SetMinimumChange(ushort value) + { + InputPort.AnalogMinChange = value; + } + + void InputPort_VersiportChange(Versiport port, VersiportEventArgs args) + { + Debug.Console(1, this, "Versiport change: {0}", args.Event); + + if(args.Event == eVersiportEvent.AnalogInChange) + InputValueFeedback.FireUpdate(); + if (args.Event == eVersiportEvent.AnalogMinChangeChange) + InputMinimumChangeFeedback.FireUpdate(); + } + + + #region Bridge Linking + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new IAnalogInputJoinMap(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."); + } + + try + { + Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + + // Link feedback for input state + InputValueFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputValue.JoinNumber]); + InputMinimumChangeFeedback.LinkInputSig(trilist.UShortInput[joinMap.MinimumChange.JoinNumber]); + trilist.SetUShortSigAction(joinMap.MinimumChange.JoinNumber, SetMinimumChange); + + } + catch (Exception e) + { + Debug.Console(1, this, "Unable to link device '{0}'. Input is null", Key); + Debug.Console(1, this, "Error: {0}", e); + } + + trilist.OnlineStatusChange += (d, args) => + { + if (!args.DeviceOnLine) return; + InputValueFeedback.FireUpdate(); + InputMinimumChangeFeedback.FireUpdate(); + }; + + } + + void trilist_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) + { + throw new NotImplementedException(); + } + + #endregion + + + public static Versiport GetVersiportDigitalInput(IOPortConfig dc) + { + + IIOPorts ioPortDevice; + + if (dc.PortDeviceKey.Equals("processor")) + { + if (!Global.ControlSystem.SupportsVersiport) + { + Debug.Console(0, "GetVersiportAnalogInput: Processor does not support Versiports"); + return null; + } + ioPortDevice = Global.ControlSystem; + } + else + { + var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts; + if (ioPortDev == null) + { + Debug.Console(0, "GetVersiportAnalogInput: Device {0} is not a valid device", dc.PortDeviceKey); + return null; + } + ioPortDevice = ioPortDev; + } + if (ioPortDevice == null) + { + Debug.Console(0, "GetVersiportAnalogInput: Device '0' is not a valid IIOPorts Device", dc.PortDeviceKey); + return null; + } + + if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts) + { + Debug.Console(0, "GetVersiportAnalogInput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber); + return null; + } + if(!ioPortDevice.VersiPorts[dc.PortNumber].SupportsAnalogInput) + { + Debug.Console(0, "GetVersiportAnalogInput: Device {0} does not support AnalogInput on port {1}", dc.PortDeviceKey, dc.PortNumber); + return null; + } + + + return ioPortDevice.VersiPorts[dc.PortNumber]; + + + } + } + + + public class GenericVersiportAbalogInputDeviceFactory : EssentialsDeviceFactory + { + public GenericVersiportAbalogInputDeviceFactory() + { + TypeNames = new List() { "versiportanaloginput" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new Generic Versiport Device"); + + var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); + + if (props == null) return null; + + var portDevice = new GenericVersiportAnalogInputDevice(dc.Key, dc.Name, GenericVersiportAnalogInputDevice.GetVersiportDigitalInput, props); + + return portDevice; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/IAnalogInput.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/IAnalogInput.cs new file mode 100644 index 00000000..44af9954 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Inputs/IAnalogInput.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Core.CrestronIO +{ + public interface IAnalogInput + { + IntFeedback InputValueFeedback { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Outputs/GenericVersiportOutputDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Outputs/GenericVersiportOutputDevice.cs index 77037845..2d1ae9c4 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Outputs/GenericVersiportOutputDevice.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Crestron IO/Outputs/GenericVersiportOutputDevice.cs @@ -42,18 +42,19 @@ namespace PepperDash.Essentials.Core.CrestronIO OutputPort = postActivationFunc(config); OutputPort.Register(); + + if (!OutputPort.SupportsDigitalOutput) { Debug.Console(0, this, "Device does not support configuration as a Digital Output"); return; } + OutputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalOutput); + + OutputPort.VersiportChange += OutputPort_VersiportChange; - - - Debug.Console(1, this, "Created GenericVersiportDigitalOutputDevice on port '{0}'.", config.PortNumber); - }); } @@ -72,7 +73,18 @@ namespace PepperDash.Essentials.Core.CrestronIO /// value to set the output to public void SetOutput(bool state) { - OutputPort.DigitalOut = state; + if (OutputPort.SupportsDigitalOutput) + { + Debug.Console(0, this, "Passed the Check"); + + OutputPort.DigitalOut = state; + + } + else + { + Debug.Console(0, this, "Versiport does not support Digital Output Mode"); + } + } #region Bridge Linking @@ -115,40 +127,40 @@ namespace PepperDash.Essentials.Core.CrestronIO public static Versiport GetVersiportDigitalOutput(IOPortConfig dc) { - - IIOPorts ioPortDevice; - if (dc.PortDeviceKey.Equals("processor")) - { - if (!Global.ControlSystem.SupportsVersiport) + IIOPorts ioPortDevice; + + if (dc.PortDeviceKey.Equals("processor")) { - Debug.Console(0, "GetVersiportDigitalOuptut: Processor does not support Versiports"); + if (!Global.ControlSystem.SupportsVersiport) + { + Debug.Console(0, "GetVersiportDigitalOuptut: Processor does not support Versiports"); + return null; + } + ioPortDevice = Global.ControlSystem; + } + else + { + var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts; + if (ioPortDev == null) + { + Debug.Console(0, "GetVersiportDigitalOuptut: Device {0} is not a valid device", dc.PortDeviceKey); + return null; + } + ioPortDevice = ioPortDev; + } + if (ioPortDevice == null) + { + Debug.Console(0, "GetVersiportDigitalOuptut: Device '0' is not a valid IOPorts Device", dc.PortDeviceKey); return null; } - ioPortDevice = Global.ControlSystem; - } - else - { - var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts; - if (ioPortDev == null) + + if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts) { - Debug.Console(0, "GetVersiportDigitalOuptut: Device {0} is not a valid device", dc.PortDeviceKey); - return null; + Debug.Console(0, "GetVersiportDigitalOuptut: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber); } - ioPortDevice = ioPortDev; - } - if (ioPortDevice == null) - { - Debug.Console(0, "GetVersiportDigitalOuptut: Device '0' is not a valid IIOPorts Device", dc.PortDeviceKey); - return null; - } - - if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts) - { - Debug.Console(0, "GetVersiportDigitalOuptut: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber); - } - - return ioPortDevice.VersiPorts[dc.PortNumber]; + var port = ioPortDevice.VersiPorts[dc.PortNumber]; + return port; } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs index 57bf2287..65efd0b9 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs @@ -388,20 +388,18 @@ namespace PepperDash.Essentials.Core var outputPorts = ((device as IRoutingOutputs) != null) ? (device as IRoutingOutputs).OutputPorts : null; if (inputPorts != null) { - Debug.Console(0, "Device {0} has {1} Input Ports:", s, inputPorts.Count); + CrestronConsole.ConsoleCommandResponse("Device {0} has {1} Input Ports:", s, inputPorts.Count); foreach (var routingInputPort in inputPorts) { - Debug.Console(0, "{0}", routingInputPort.Key); - } - } - if (outputPorts != null) - { - Debug.Console(0, "Device {0} has {1} Output Ports:", s, outputPorts.Count); - foreach (var routingOutputPort in outputPorts) - { - Debug.Console(0, "{0}", routingOutputPort.Key); + CrestronConsole.ConsoleCommandResponse("{0}", routingInputPort.Key); } } + if (outputPorts == null) return; + CrestronConsole.ConsoleCommandResponse("Device {0} has {1} Output Ports:", s, outputPorts.Count); + foreach (var routingOutputPort in outputPorts) + { + CrestronConsole.ConsoleCommandResponse("{0}", routingOutputPort.Key); + } } /// @@ -435,7 +433,7 @@ namespace PepperDash.Essentials.Core if (device == null) { - Debug.Console(0, "Unable to get device with key: {0}", deviceKey); + CrestronConsole.ConsoleCommandResponse("Unable to get device with key: {0}", deviceKey); return; } @@ -447,7 +445,7 @@ namespace PepperDash.Essentials.Core } catch { - Debug.Console(0, "Unable to convert setting value. Please use off/rx/tx/both"); + CrestronConsole.ConsoleCommandResponse("Unable to convert setting value. Please use off/rx/tx/both"); return; } @@ -458,18 +456,18 @@ namespace PepperDash.Essentials.Core var min = Convert.ToUInt32(timeout); device.StreamDebugging.SetDebuggingWithSpecificTimeout(debugSetting, min); - Debug.Console(0, "Device: '{0}' debug level set to {1} for {2} minutes", deviceKey, debugSetting, min); + CrestronConsole.ConsoleCommandResponse("Device: '{0}' debug level set to {1} for {2} minutes", deviceKey, debugSetting, min); } catch (Exception e) { - Debug.Console(0, "Unable to convert minutes or settings value. Please use an integer value for minutes. Errro: {0}", e); + CrestronConsole.ConsoleCommandResponse("Unable to convert minutes or settings value. Please use an integer value for minutes. Error: {0}", e); } } else { device.StreamDebugging.SetDebuggingWithDefaultTimeout(debugSetting); - Debug.Console(0, "Device: '{0}' debug level set to {1} for default time (30 minutes)", deviceKey, debugSetting); + CrestronConsole.ConsoleCommandResponse("Device: '{0}' debug level set to {1} for default time (30 minutes)", deviceKey, debugSetting); } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs index bad3e531..793ff34a 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs @@ -172,21 +172,14 @@ namespace PepperDash.Essentials.Core /// /// Prints the type names and associated metadata from the FactoryMethods collection. /// - /// + /// public static void GetDeviceFactoryTypes(string filter) { - Dictionary types = new Dictionary(); + var types = !string.IsNullOrEmpty(filter) + ? FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value) + : FactoryMethods; - if (!string.IsNullOrEmpty(filter)) - { - types = FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value); - } - else - { - types = FactoryMethods; - } - - Debug.Console(0, "Device Types:"); + CrestronConsole.ConsoleCommandResponse("Device Types:"); foreach (var type in types.OrderBy(t => t.Key)) { @@ -198,7 +191,7 @@ namespace PepperDash.Essentials.Core cType = type.Value.CType.FullName; } - Debug.Console(0, + CrestronConsole.ConsoleCommandResponse( @"Type: '{0}' CType: '{1}' Description: {2}", type.Key, cType, description); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs index 67a5740f..60aa6275 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Data; using System.Globalization; using System.Linq; -using System.Runtime.InteropServices; using System.Text; using Crestron.SimplSharp.Reflection; using Crestron.SimplSharp.CrestronIO; @@ -103,22 +101,22 @@ namespace PepperDash.Essentials.Core /// public void PrintJoinMapInfo() { - Debug.Console(0, "{0}:\n", GetType().Name); + CrestronConsole.ConsoleCommandResponse("{0}:\n", GetType().Name); // Get the joins of each type and print them - Debug.Console(0, "Digitals:"); + CrestronConsole.ConsoleCommandResponse("Digitals:"); var digitals = Joins.Where(j => (j.Value.JoinType & eJoinType.Digital) == eJoinType.Digital).ToDictionary(j => j.Key, j => j.Value); - Debug.Console(2, "Found {0} Digital Joins", digitals.Count); + CrestronConsole.ConsoleCommandResponse("Found {0} Digital Joins", digitals.Count); PrintJoinList(GetSortedJoins(digitals)); - Debug.Console(0, "Analogs:"); + CrestronConsole.ConsoleCommandResponse("Analogs:"); var analogs = Joins.Where(j => (j.Value.JoinType & eJoinType.Analog) == eJoinType.Analog).ToDictionary(j => j.Key, j => j.Value); - Debug.Console(2, "Found {0} Analog Joins", analogs.Count); + CrestronConsole.ConsoleCommandResponse("Found {0} Analog Joins", analogs.Count); PrintJoinList(GetSortedJoins(analogs)); - Debug.Console(0, "Serials:"); + CrestronConsole.ConsoleCommandResponse("Serials:"); var serials = Joins.Where(j => (j.Value.JoinType & eJoinType.Serial) == eJoinType.Serial).ToDictionary(j => j.Key, j => j.Value); - Debug.Console(2, "Found {0} Serial Joins", serials.Count); + CrestronConsole.ConsoleCommandResponse("Found {0} Serial Joins", serials.Count); PrintJoinList(GetSortedJoins(serials)); } @@ -141,7 +139,7 @@ namespace PepperDash.Essentials.Core { foreach (var join in joins) { - Debug.Console(0, + CrestronConsole.ConsoleCommandResponse( @"Join Number: {0} | Label: '{1}' | JoinSpan: '{2}' | Type: '{3}' | Capabilities: '{4}'", join.Value.JoinNumber, join.Value.Label, @@ -236,25 +234,45 @@ namespace PepperDash.Essentials.Core /// public void PrintJoinMapInfo() { - Debug.Console(0, "{0}:\n", GetType().Name); + var sb = JoinmapStringBuilder(); + + CrestronConsole.ConsoleCommandResponse(sb.ToString()); + } + + private StringBuilder JoinmapStringBuilder() + { + var sb = new StringBuilder(); // Get the joins of each type and print them - Debug.Console(0, "Digitals:"); - var digitals = Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Digital) == eJoinType.Digital).ToDictionary(j => j.Key, j => j.Value); - Debug.Console(2, "Found {0} Digital Joins", digitals.Count); - PrintJoinList(GetSortedJoins(digitals)); + sb.AppendLine(String.Format("# {0}", GetType().Name)); + sb.AppendLine(); + sb.AppendLine("## Digitals"); + sb.AppendLine(); + // Get the joins of each type and print them + var digitals = + Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Digital) == eJoinType.Digital) + .ToDictionary(j => j.Key, j => j.Value); + var digitalSb = AppendJoinList(GetSortedJoins(digitals)); + digitalSb.AppendLine("## Analogs"); + digitalSb.AppendLine(); - Debug.Console(0, "Analogs:"); - var analogs = Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Analog) == eJoinType.Analog).ToDictionary(j => j.Key, j => j.Value); - Debug.Console(2, "Found {0} Analog Joins", analogs.Count); - PrintJoinList(GetSortedJoins(analogs)); + var analogs = + Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Analog) == eJoinType.Analog) + .ToDictionary(j => j.Key, j => j.Value); + var analogSb = AppendJoinList(GetSortedJoins(analogs)); + analogSb.AppendLine("## Serials"); + analogSb.AppendLine(); - Debug.Console(0, "Serials:"); - var serials = Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Serial) == eJoinType.Serial).ToDictionary(j => j.Key, j => j.Value); - Debug.Console(2, "Found {0} Serial Joins", serials.Count); - PrintJoinList(GetSortedJoins(serials)); + var serials = + Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Serial) == eJoinType.Serial) + .ToDictionary(j => j.Key, j => j.Value); + var serialSb = AppendJoinList(GetSortedJoins(serials)); + sb.EnsureCapacity(sb.Length + digitalSb.Length + analogSb.Length + serialSb.Length); + sb.Append(digitalSb).Append(analogSb).Append(serialSb); + return sb; } + /// /// Prints the join information to console /// @@ -262,37 +280,11 @@ namespace PepperDash.Essentials.Core { var pluginType = GetType().Name; - Debug.Console(0, "{0}:\n", pluginType); + CrestronConsole.ConsoleCommandResponse("{0}:\n", pluginType); - var sb = new StringBuilder(); - sb.AppendLine(String.Format("# {0}", GetType().Name)); - sb.AppendLine(String.Format("Generated from '{0}' on bridge '{1}'", deviceKey, bridgeKey)); - sb.AppendLine(); - sb.AppendLine("## Digitals"); - // Get the joins of each type and print them - var digitals = Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Digital) == eJoinType.Digital).ToDictionary(j => j.Key, j => j.Value); - Debug.Console(2, "Found {0} Digital Joins", digitals.Count); - var digitalSb = AppendJoinList(GetSortedJoins(digitals)); - digitalSb.AppendLine("## Analogs"); - digitalSb.AppendLine(); - Debug.Console(0, "Analogs:"); - var analogs = Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Analog) == eJoinType.Analog).ToDictionary(j => j.Key, j => j.Value); - Debug.Console(2, "Found {0} Analog Joins", analogs.Count); - var analogSb = AppendJoinList(GetSortedJoins(analogs)); - analogSb.AppendLine("## Serials"); - analogSb.AppendLine(); - - Debug.Console(0, "Serials:"); - var serials = Joins.Where(j => (j.Value.Metadata.JoinType & eJoinType.Serial) == eJoinType.Serial).ToDictionary(j => j.Key, j => j.Value); - Debug.Console(2, "Found {0} Serial Joins", serials.Count); - var serialSb = AppendJoinList(GetSortedJoins(serials)); - - sb.EnsureCapacity(sb.Length + digitalSb.Length + analogSb.Length + serialSb.Length); - sb.Append(digitalSb).Append(analogSb).Append(serialSb); - - WriteJoinmapMarkdown(sb, pluginType, bridgeKey, deviceKey); + WriteJoinmapMarkdown(JoinmapStringBuilder(), pluginType, bridgeKey, deviceKey); } @@ -303,7 +295,7 @@ namespace PepperDash.Essentials.Core using (var sw = new StreamWriter(fileName)) { sw.WriteLine(stringBuilder.ToString()); - Debug.Console(0, "Joinmap Readme generated and written to {0}", fileName); + CrestronConsole.ConsoleCommandResponse("Joinmap Readme generated and written to {0}", fileName); } } @@ -313,7 +305,7 @@ namespace PepperDash.Essentials.Core /// /// /// - List> GetSortedJoins(Dictionary joins) + static List> GetSortedJoins(Dictionary joins) { var sortedJoins = joins.ToList(); @@ -322,20 +314,6 @@ namespace PepperDash.Essentials.Core return sortedJoins; } - void PrintJoinList(List> joins) - { - foreach (var join in joins) - { - Debug.Console(0, - @"Join Number: {0} | JoinSpan: '{1}' | JoinName: {2} | Description: '{3}' | Type: '{4}' | Capabilities: '{5}'", - join.Value.JoinNumber, - join.Value.JoinSpan, - join.Key, - String.IsNullOrEmpty(join.Value.AttributeName) ? join.Value.Metadata.Label : join.Value.AttributeName, - join.Value.Metadata.JoinType.ToString(), - join.Value.Metadata.JoinCapabilities.ToString()); - } - } static StringBuilder AppendJoinList(List> joins) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 9c4a73c5..c8ecbde6 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -127,6 +127,7 @@ + @@ -186,8 +187,10 @@ + + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs index 9da843b8..f69d35d0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs @@ -194,10 +194,11 @@ namespace PepperDash.Essentials /// public static void ReportAssemblyVersions(string command) { - Debug.Console(0, "Loaded Assemblies:"); + + CrestronConsole.ConsoleCommandResponse("Loaded Assemblies:"); foreach (var assembly in LoadedAssemblies) { - Debug.Console(0, "{0} Version: {1}", assembly.Name, assembly.Version); + CrestronConsole.ConsoleCommandResponse("{0} Version: {1}", assembly.Name, assembly.Version); } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/IRoutingInputsExtensions.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/IRoutingInputsExtensions.cs index d371c2d6..4cd0d4ff 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/IRoutingInputsExtensions.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/IRoutingInputsExtensions.cs @@ -1,425 +1,425 @@ -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 RouteRequest - { - public IRoutingSink Destination {get; set;} - public IRoutingOutputs Source {get; set;} - public eRoutingSignalType SignalType {get; set;} - - public void HandleCooldown(object sender, FeedbackEventArgs args) - { - var coolingDevice = sender as IWarmingCooling; - - if(args.BoolValue == false) - { - Destination.ReleaseAndMakeRoute(Source, SignalType); - - if(sender == null) return; - - coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown; - } - } - } - - /// - /// Extensions added to any IRoutingInputs classes to provide discovery-based routing - /// on those destinations. - /// - public static class IRoutingInputsExtensions - { - private static Dictionary RouteRequests = new Dictionary(); - /// - /// 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 IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType) - { - var routeRequest = new RouteRequest { - Destination = destination, - Source = source, - SignalType = signalType - }; - - var coolingDevice = destination as IWarmingCooling; - - RouteRequest existingRouteRequest; - - //We already have a route request for this device, and it's a cooling device and is cooling - if (RouteRequests.TryGetValue(destination.Key, out existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true) - { - coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown; - - coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown; - +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 RouteRequest + { + public IRoutingSink Destination {get; set;} + public IRoutingOutputs Source {get; set;} + public eRoutingSignalType SignalType {get; set;} + + public void HandleCooldown(object sender, FeedbackEventArgs args) + { + var coolingDevice = sender as IWarmingCooling; + + if(args.BoolValue == false) + { + Destination.ReleaseAndMakeRoute(Source, SignalType); + + if(sender == null) return; + + coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown; + } + } + } + + /// + /// Extensions added to any IRoutingInputs classes to provide discovery-based routing + /// on those destinations. + /// + public static class IRoutingInputsExtensions + { + private static Dictionary RouteRequests = new Dictionary(); + /// + /// 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 IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType) + { + var routeRequest = new RouteRequest { + Destination = destination, + Source = source, + SignalType = signalType + }; + + var coolingDevice = destination as IWarmingCooling; + + RouteRequest existingRouteRequest; + + //We already have a route request for this device, and it's a cooling device and is cooling + if (RouteRequests.TryGetValue(destination.Key, out existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true) + { + coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown; + + coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown; + RouteRequests[destination.Key] = routeRequest; - Debug.Console(2, "******************************************************** Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", destination.Key, routeRequest.Source.Key); - - return; - } - - //New Request - if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true) - { - coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown; - - coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown; - + Debug.Console(2, "******************************************************** Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", destination.Key, routeRequest.Source.Key); + + return; + } + + //New Request + if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true) + { + coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown; + + coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown; + RouteRequests.Add(destination.Key, routeRequest); Debug.Console(2, "******************************************************** Device: {0} is cooling down. Storing route request to route to source key: {1}", destination.Key, routeRequest.Source.Key); - return; - } - - if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false) - { + return; + } + + if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false) + { RouteRequests.Remove(destination.Key); Debug.Console(2, "******************************************************** Device: {0} is NOT cooling down. Removing stored route request and routing to source key: {1}", destination.Key, routeRequest.Source.Key); - } - - destination.ReleaseRoute(); - - RunRouteRequest(routeRequest); - } - - public static void RunRouteRequest(RouteRequest request) - { - if (request.Source == null) return; - var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType); - if (newRoute == null) return; - RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute); - Debug.Console(2, request.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 IRoutingSink destination) - { - RouteRequest existingRequest; - - if (RouteRequests.TryGetValue(destination.Key, out existingRequest) && destination is IWarmingCooling) - { - var coolingDevice = destination as IWarmingCooling; - - coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown; - } - - RouteRequests.Remove(destination.Key); - - var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination); - if (current != null) - { - Debug.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 IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType) - { - var routeDescr = new RouteDescriptor(source, destination, signalType); - // if it's a single signal type, find the route - if ((signalType & (eRoutingSignalType.Audio & eRoutingSignalType.Video)) == (eRoutingSignalType.Audio & eRoutingSignalType.Video)) - { - Debug.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.Audio | eRoutingSignalType.Video)) == (eRoutingSignalType.Audio | eRoutingSignalType.Video))); - - // find a direct tie - var directTie = destDevInputTies.FirstOrDefault( - t => t.DestinationPort.ParentDevice == destination - && t.SourcePort.ParentDevice == source); - if (directTie != null) // Found a tie directly to the source - { - goodInputPort = directTie.DestinationPort; - } - else // no direct-connect. Walk back devices. - { - Debug.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); - - //Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration - if (alreadyCheckedDevices == null) - alreadyCheckedDevices = new List(); - alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs); - - foreach (var inputTieToTry in attachedMidpoints) - { - var upstreamDeviceOutputPort = inputTieToTry.SourcePort; - var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs; - Debug.Console(2, destination, "Trying to find route on {0}", upstreamRoutingDevice.Key); - - // Check if this previous device has already been walked - if (alreadyCheckedDevices.Contains(upstreamRoutingDevice)) - { - Debug.Console(2, destination, "Skipping input {0} on {1}, this was already checked", upstreamRoutingDevice.Key, destination.Key); - continue; - } - // haven't seen this device yet. Do it. Pass the output port to the next - // level to enable switching on success - var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort, - alreadyCheckedDevices, signalType, cycle, routeTable); - if (upstreamRoutingSuccess) - { - Debug.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; - } - - 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 IRoutingSink) - { - var device = route.SwitchingDevice as IRoutingSinkWithSwitching; - if (device == null) - continue; - - device.ExecuteSwitch(route.InputPort.Selector); - } - else if (route.SwitchingDevice is IRouting) - { - (route.SwitchingDevice as IRouting).ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType); - route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType); - Debug.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); - - } - } + } + + destination.ReleaseRoute(); + + RunRouteRequest(routeRequest); + } + + public static void RunRouteRequest(RouteRequest request) + { + if (request.Source == null) return; + var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType); + if (newRoute == null) return; + RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute); + Debug.Console(2, request.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 IRoutingSink destination) + { + RouteRequest existingRequest; + + if (RouteRequests.TryGetValue(destination.Key, out existingRequest) && destination is IWarmingCooling) + { + var coolingDevice = destination as IWarmingCooling; + + coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown; + } + + RouteRequests.Remove(destination.Key); + + var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination); + if (current != null) + { + Debug.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 IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType) + { + var routeDescr = new RouteDescriptor(source, destination, signalType); + // if it's a single signal type, find the route + if ((signalType & (eRoutingSignalType.Audio & eRoutingSignalType.Video)) == (eRoutingSignalType.Audio & eRoutingSignalType.Video)) + { + Debug.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.Audio | eRoutingSignalType.Video)) == (eRoutingSignalType.Audio | eRoutingSignalType.Video))); + + // find a direct tie + var directTie = destDevInputTies.FirstOrDefault( + t => t.DestinationPort.ParentDevice == destination + && t.SourcePort.ParentDevice == source); + if (directTie != null) // Found a tie directly to the source + { + goodInputPort = directTie.DestinationPort; + } + else // no direct-connect. Walk back devices. + { + Debug.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); + + //Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration + if (alreadyCheckedDevices == null) + alreadyCheckedDevices = new List(); + alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs); + + foreach (var inputTieToTry in attachedMidpoints) + { + var upstreamDeviceOutputPort = inputTieToTry.SourcePort; + var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs; + Debug.Console(2, destination, "Trying to find route on {0}", upstreamRoutingDevice.Key); + + // Check if this previous device has already been walked + if (alreadyCheckedDevices.Contains(upstreamRoutingDevice)) + { + Debug.Console(2, destination, "Skipping input {0} on {1}, this was already checked", upstreamRoutingDevice.Key, destination.Key); + continue; + } + // haven't seen this device yet. Do it. Pass the output port to the next + // level to enable switching on success + var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort, + alreadyCheckedDevices, signalType, cycle, routeTable); + if (upstreamRoutingSuccess) + { + Debug.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; + } + + 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 IRoutingSink) + { + var device = route.SwitchingDevice as IRoutingSinkWithSwitching; + if (device == null) + continue; + + device.ExecuteSwitch(route.InputPort.Selector); + } + else if (route.SwitchingDevice is IRouting) + { + (route.SwitchingDevice as IRouting).ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType); + route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType); + Debug.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 Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs index 19c61b9f..c0048a68 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs @@ -1004,7 +1004,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec if (codec.DirectoryRoot != null) { - trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)codec.DirectoryRoot.CurrentDirectoryResults.Count); + var contactsCount = codec.DirectoryRoot.CurrentDirectoryResults.Where(c => c.ParentFolderId.Equals("root")).ToList().Count; + trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)contactsCount); + Debug.Console(2, this, ">>> contactsCount: {0}", contactsCount); var clearBytes = XSigHelpers.ClearOutputs(); @@ -1020,7 +1022,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec codec.DirectoryResultReturned += (sender, args) => { - trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)args.Directory.CurrentDirectoryResults.Count); + var isRoot = codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false; + var argsCount = isRoot + ? args.Directory.CurrentDirectoryResults.Where(a => a.ParentFolderId.Equals("root")).ToList().Count + : args.Directory.CurrentDirectoryResults.Count; + + trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)argsCount); + Debug.Console(2, this, ">>> argsCount: {0}", argsCount); var clearBytes = XSigHelpers.ClearOutputs(); @@ -1184,46 +1192,47 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec return GetXSigString(tokenArray); } - private string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot) - { - var xSigMaxIndex = 1023; - var tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count > xSigMaxIndex - ? xSigMaxIndex - : directory.CurrentDirectoryResults.Count]; + private string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot) + { + var xSigMaxIndex = 1023; + var tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count > xSigMaxIndex + ? xSigMaxIndex + : directory.CurrentDirectoryResults.Count]; - Debug.Console(2, this, "IsRoot: {0}, Directory Count: {1}, TokenArray.Length: {2}", isRoot, - directory.CurrentDirectoryResults.Count, tokenArray.Length); + Debug.Console(2, this, "IsRoot: {0}, Directory Count: {1}, TokenArray.Length: {2}", isRoot, directory.CurrentDirectoryResults.Count, tokenArray.Length); - var contacts = directory.CurrentDirectoryResults.Count > xSigMaxIndex - ? directory.CurrentDirectoryResults.Take(xSigMaxIndex) - : directory.CurrentDirectoryResults; + var contacts = directory.CurrentDirectoryResults.Count > xSigMaxIndex + ? directory.CurrentDirectoryResults.Take(xSigMaxIndex) + : directory.CurrentDirectoryResults; - var counterIndex = 1; - foreach (var entry in contacts) - { - var arrayIndex = counterIndex - 1; - var entryIndex = counterIndex; + var contactsToDisplay = isRoot + ? contacts.Where(c => c.ParentFolderId == "root") + : contacts.Where(c => c.ParentFolderId != "root"); - Debug.Console(2, this, "Entry{2:0000} Name: {0}, Folder ID: {1}", entry.Name, entry.FolderId, entryIndex); + var counterIndex = 1; + foreach (var entry in contactsToDisplay) + { + var arrayIndex = counterIndex - 1; + var entryIndex = counterIndex; - if (entry is DirectoryFolder && entry.ParentFolderId == "root") - { - tokenArray[arrayIndex] = new XSigSerialToken(entryIndex, String.Format("[+] {0}", entry.Name)); + Debug.Console(2, this, "Entry{2:0000} Name: {0}, Folder ID: {1}, Type: {3}, ParentFolderId: {4}", + entry.Name, entry.FolderId, entryIndex, entry.GetType().GetCType().FullName, entry.ParentFolderId); - counterIndex++; - counterIndex++; + if (entry is DirectoryFolder) + { + tokenArray[arrayIndex] = new XSigSerialToken(entryIndex, String.Format("[+] {0}", entry.Name)); - continue; - } + counterIndex++; - tokenArray[arrayIndex] = new XSigSerialToken(entryIndex, entry.Name); + continue; + } - counterIndex++; - } - - return GetXSigString(tokenArray); + tokenArray[arrayIndex] = new XSigSerialToken(entryIndex, entry.Name); + counterIndex++; + } + return GetXSigString(tokenArray); } private void LinkVideoCodecCallControlsToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) 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 index 0075b657..79a5395d 100644 --- 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 @@ -303,11 +303,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { var contact = new InvitableDirectoryContact { Name = c.ScreenName, ContactId = c.Jid }; - contact.ContactMethods.Add(new ContactMethod() { Number = c.Jid, Device = eContactMethodDevice.Video, CallType = eContactMethodCallType.Video, ContactMethodId = c.Jid }); + contact.ContactMethods.Add(new ContactMethod() + { + Number = c.Jid, + Device = eContactMethodDevice.Video, + CallType = eContactMethodCallType.Video, + ContactMethodId = c.Jid + }); if (folders.Count > 0) { - contact.ParentFolderId = c.IsZoomRoom ? "rooms" : "contacts"; + contact.ParentFolderId = c.IsZoomRoom + ? roomFolder.FolderId // "rooms" + : contactFolder.FolderId; // "contacts" } contacts.Add(contact); diff --git a/packages.config b/packages.config index e9ea5933..104d002d 100644 --- a/packages.config +++ b/packages.config @@ -1,3 +1,3 @@ - + \ No newline at end of file