diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 9a48000a..1ab3b28e 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -83,10 +83,10 @@ namespace PepperDash.Essentials CrestronConsole.AddNewConsoleCommand(BridgeHelper.PrintJoinMap, "getjoinmap", "map(s) for bridge or device on bridge [brKey [devKey]]", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "CONSOLE MESSAGE: {0}", s); - }, "appdebugmessage", "Writes message to log", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(BridgeHelper.JoinmapMarkdown, "getjoinmapmarkdown" + , "generate markdown of map(s) for bridge or device on bridge [brKey [devKey]]", ConsoleAccessLevelEnum.AccessOperator); + + CrestronConsole.AddNewConsoleCommand(s => Debug.Console(0, Debug.ErrorLogLevel.Notice, "CONSOLE MESSAGE: {0}", s), "appdebugmessage", "Writes message to log", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(s => { @@ -103,12 +103,16 @@ namespace PepperDash.Essentials (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\n" + - "System URL: {0}\r\n" + - "Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl); - }, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => + CrestronConsole.ConsoleCommandResponse( + "This system can be found at the following URLs:\r\n" + + "System URL: {0}\r\n" + + "Template URL: {1}", + ConfigReader.ConfigObject.SystemUrl, + ConfigReader.ConfigObject.TemplateUrl), + "portalinfo", + "Shows portal URLS from configuration", + ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(DeviceManager.GetRoutingPorts, @@ -298,6 +302,10 @@ namespace PepperDash.Essentials if (!Directory.Exists(pluginDir)) Directory.Create(pluginDir); + var joinmapDir = Global.FilePathPrefix + "joinmaps"; + if(!Directory.Exists(joinmapDir)) + Directory.Create(joinmapDir); + return configExists; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs index 252c9c48..11b8f4ff 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs @@ -46,6 +46,33 @@ namespace PepperDash.Essentials.Core.Bridges bridge.PrintJoinMaps(); } } + public static void JoinmapMarkdown(string command) + { + var targets = command.Split(' '); + + var bridgeKey = targets[0].Trim(); + + var bridge = DeviceManager.GetDeviceForKey(bridgeKey) as EiscApiAdvanced; + + if (bridge == null) + { + Debug.Console(0, "Unable to find advanced bridge with key: '{0}'", bridgeKey); + return; + } + + if (targets.Length > 1) + { + var deviceKey = targets[1].Trim(); + + if (string.IsNullOrEmpty(deviceKey)) return; + bridge.MarkdownJoinMapForDevice(deviceKey, bridgeKey); + } + else + { + bridge.MarkdownForBridge(bridgeKey); + + } + } } @@ -227,6 +254,19 @@ namespace PepperDash.Essentials.Core.Bridges joinMap.Value.PrintJoinMapInfo(); } } + /// + /// Generates markdown for all join maps on this bridge + /// + public virtual void MarkdownForBridge(string bridgeKey) + { + Debug.Console(0, this, "Writing Joinmaps to files for EISC IPID: {0}", Eisc.ID.ToString("X")); + + foreach (var joinMap in JoinMaps) + { + Debug.Console(0, "Generating markdown for device '{0}':", joinMap.Key); + joinMap.Value.MarkdownJoinMapInfo(joinMap.Key, bridgeKey); + } + } /// /// Prints the join map for a device by key @@ -242,9 +282,26 @@ namespace PepperDash.Essentials.Core.Bridges return; } - Debug.Console(0, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key); + Debug.Console(0, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key); joinMap.PrintJoinMapInfo(); } + /// + /// Prints the join map for a device by key + /// + /// + public void MarkdownJoinMapForDevice(string deviceKey, string bridgeKey) + { + var joinMap = JoinMaps[deviceKey]; + + if (joinMap == null) + { + Debug.Console(0, this, "Unable to find joinMap for device with key: '{0}'", deviceKey); + return; + } + + Debug.Console(0, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key); + joinMap.MarkdownJoinMapInfo(deviceKey, bridgeKey); + } /// /// Used for debugging to trigger an action based on a join number and type diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/StringExtensions.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/StringExtensions.cs index 708ed930..39501387 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/StringExtensions.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Extensions/StringExtensions.cs @@ -16,5 +16,9 @@ namespace PepperDash.Essentials.Core { return string.IsNullOrEmpty(s.Trim()) ? null : s; } + public static string ReplaceIfNullOrEmpty(this string s, string newString) + { + return string.IsNullOrEmpty(s) ? newString : s; + } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs index 04abc7f8..6ffb16cc 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs @@ -1,8 +1,13 @@ 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; +using Crestron.SimplSharp; using PepperDash.Core; using PepperDash.Essentials.Core.Config; @@ -193,19 +198,6 @@ namespace PepperDash.Essentials.Core protected void AddJoins(Type type) { - // Add all the JoinDataComplete properties to the Joins Dictionary and pass in the offset - //Joins = this.GetType() - // .GetCType() - // .GetFields(BindingFlags.Public | BindingFlags.Instance) - // .Where(field => field.IsDefined(typeof(JoinNameAttribute), true)) - // .Select(field => (JoinDataComplete)field.GetValue(this)) - // .ToDictionary(join => join.GetNameAttribute(), join => - // { - // join.SetJoinOffset(_joinOffset); - // return join; - // }); - - //type = this.GetType(); <- this wasn't working because 'this' was always the base class, never the derived class var fields = type.GetCType() .GetFields(BindingFlags.Public | BindingFlags.Instance) @@ -219,7 +211,7 @@ namespace PepperDash.Essentials.Core if (value == null) { - Debug.Console(0, "Unable to caset base class to {0}", type.Name); + Debug.Console(0, "Unable to cast base class to {0}", type.Name); continue; } @@ -256,12 +248,64 @@ namespace PepperDash.Essentials.Core 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)); - + 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)); + } + /// + /// Prints the join information to console + /// + public void MarkdownJoinMapInfo(string deviceKey, string bridgeKey) + { + var pluginType = GetType().Name; + + Debug.Console(0, "{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); + + } + + private static void WriteJoinmapMarkdown(StringBuilder stringBuilder, string pluginType, string bridgeKey, string deviceKey) + { + var fileName = String.Format("{0}{1}{2}__{3}__{4}.md", Global.FilePathPrefix, "joinMaps/", pluginType, bridgeKey, deviceKey); + + using (var sw = new StreamWriter(fileName)) + { + sw.WriteLine(stringBuilder.ToString()); + Debug.Console(0, "Joinmap Readme generated and written to {0}", fileName); + } + } /// @@ -293,6 +337,39 @@ namespace PepperDash.Essentials.Core } } + static StringBuilder AppendJoinList(List> joins) + { + var sb = new StringBuilder(); + const string stringFormatter = "| {0} | {1} | {2} | {3} | {4} |"; + const int joinNumberLen = 11; + const int joinSpanLen = 9; + const int typeLen = 19; + const int capabilitiesLen = 12; + var descriptionLen = (from @join in joins select @join.Value into j select j.Metadata.Description.Length).Concat(new[] {11}).Max(); + + //build header + sb.AppendLine(String.Format(stringFormatter, + String.Format("Join Number").PadRight(joinNumberLen, ' '), + String.Format("Join Span").PadRight(joinSpanLen, ' '), + String.Format("Description").PadRight(descriptionLen, ' '), + String.Format("Type").PadRight(typeLen, ' '), + String.Format("Capabilities").PadRight(capabilitiesLen, ' '))); + //build table seperator + sb.AppendLine(String.Format(stringFormatter, + new String('-', joinNumberLen), + new String('-', joinSpanLen), + new String('-', descriptionLen), + new String('-', typeLen), + new String('-', capabilitiesLen))); + + foreach (var join in joins) + { + sb.AppendLine(join.Value.GetMarkdownFormattedData(stringFormatter, descriptionLen)); + } + sb.AppendLine(); + return sb; + } + /// /// Attempts to find the matching key for the custom join and if found overwrites the default JoinData with the custom /// @@ -459,6 +536,64 @@ namespace PepperDash.Essentials.Core Metadata = metadata; } + public string GetMarkdownFormattedData(string stringFormatter, int descriptionLen) + { + + //Fixed Width Headers + var joinNumberLen = String.Format("Join Number").Length; + var joinSpanLen = String.Format("Join Span").Length; + var typeLen = String.Format("AnalogDigitalSerial").Length; + var capabilitiesLen = String.Format("ToFromFusion").Length; + + //Track which one failed, if it did + const string placeholder = "unknown"; + var dataArray = new Dictionary + { + {"joinNumber", placeholder.PadRight(joinNumberLen, ' ')}, + {"joinSpan", placeholder.PadRight(joinSpanLen, ' ')}, + {"description", placeholder.PadRight(descriptionLen, ' ')}, + {"joinType", placeholder.PadRight(typeLen, ' ')}, + {"capabilities", placeholder.PadRight(capabilitiesLen, ' ')} + }; + + + try + { + dataArray["joinNumber"] = String.Format("{0}", JoinNumber.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinNumberLen, ' '); + dataArray["joinSpan"] = String.Format("{0}", JoinSpan.ToString(CultureInfo.InvariantCulture).ReplaceIfNullOrEmpty(placeholder)).PadRight(joinSpanLen, ' '); + dataArray["description"] = String.Format("{0}", Metadata.Description.ReplaceIfNullOrEmpty(placeholder)).PadRight(descriptionLen, ' '); + dataArray["joinType"] = String.Format("{0}", Metadata.JoinType.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(typeLen, ' '); + dataArray["capabilities"] = String.Format("{0}", Metadata.JoinCapabilities.ToString().ReplaceIfNullOrEmpty(placeholder)).PadRight(capabilitiesLen, ' '); + + return String.Format(stringFormatter, + dataArray["joinNumber"], + dataArray["joinSpan"], + dataArray["description"], + dataArray["joinType"], + dataArray["capabilities"]); + + } + catch (Exception e) + { + //Don't Throw - we don't want to kill the system if this falls over - it's not mission critical. Print the error, use placeholder data + var errorKey = string.Empty; + foreach (var item in dataArray) + { + if (item.Value.TrimEnd() == placeholder) ; + errorKey = item.Key; + break; + } + Debug.Console(0, "Unable to decode join metadata {1}- {0}", e.Message, !String.IsNullOrEmpty(errorKey) ? (' ' + errorKey) : String.Empty); + return String.Format(stringFormatter, + dataArray["joinNumber"], + dataArray["joinSpan"], + dataArray["description"], + dataArray["joinType"], + dataArray["capabilities"]); + } + } + + /// /// Sets the join offset value /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs index 95a94a24..8e0cbc55 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Secrets/SecretsManager.cs @@ -148,6 +148,7 @@ namespace PepperDash.Essentials.Core { Secrets.Add(key, provider); Debug.Console(1, "Secrets provider '{0}' added to SecretsManager", key); + return; } Debug.Console(0, Debug.ErrorLogLevel.Notice, "Unable to add Provider '{0}' to Secrets. Provider with that key already exists", key ); } @@ -164,13 +165,13 @@ namespace PepperDash.Essentials.Core { Secrets.Add(key, provider); Debug.Console(1, "Secrets provider '{0}' added to SecretsManager", key); - + return; } if (overwrite) { Secrets.Add(key, provider); Debug.Console(1, Debug.ErrorLogLevel.Notice, "Provider with the key '{0}' already exists in secrets. Overwriting with new secrets provider.", key); - + return; } Debug.Console(0, Debug.ErrorLogLevel.Notice, "Unable to add Provider '{0}' to Secrets. Provider with that key already exists", key); }