diff --git a/src/PepperDash.Core/Config/PortalConfigReader.cs b/src/PepperDash.Core/Config/PortalConfigReader.cs index 3094e5df..a8ad9cbf 100644 --- a/src/PepperDash.Core/Config/PortalConfigReader.cs +++ b/src/PepperDash.Core/Config/PortalConfigReader.cs @@ -1,7 +1,7 @@ using System; +using System.IO; using System.Linq; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; @@ -12,223 +12,223 @@ namespace PepperDash.Core.Config; /// /// Reads a Portal formatted config file /// - public class PortalConfigReader +public class PortalConfigReader +{ + /// + /// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object. + /// + /// JObject of config file + public static void ReadAndMergeFileIfNecessary(string filePath, string savePath) { - /// - /// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object. - /// - /// JObject of config file - public static void ReadAndMergeFileIfNecessary(string filePath, string savePath) + try { - try + if (!File.Exists(filePath)) { - if (!File.Exists(filePath)) + Debug.Console(1, Debug.ErrorLogLevel.Error, + "ERROR: Configuration file not present. Please load file to {0} and reset program", filePath); + } + + using (StreamReader fs = new StreamReader(filePath)) + { + var jsonObj = JObject.Parse(fs.ReadToEnd()); + if (jsonObj["template"] != null && jsonObj["system"] != null) { - Debug.Console(1, Debug.ErrorLogLevel.Error, - "ERROR: Configuration file not present. Please load file to {0} and reset program", filePath); + // it's a double-config, merge it. + var merged = MergeConfigs(jsonObj); + if (jsonObj["system_url"] != null) + { + merged["systemUrl"] = jsonObj["system_url"].Value(); + } + + if (jsonObj["template_url"] != null) + { + merged["templateUrl"] = jsonObj["template_url"].Value(); + } + + jsonObj = merged; } - using (StreamReader fs = new StreamReader(filePath)) + using (StreamWriter fw = new StreamWriter(savePath)) { - var jsonObj = JObject.Parse(fs.ReadToEnd()); - if(jsonObj["template"] != null && jsonObj["system"] != null) - { - // it's a double-config, merge it. - var merged = MergeConfigs(jsonObj); - if (jsonObj["system_url"] != null) - { - merged["systemUrl"] = jsonObj["system_url"].Value(); - } - - if (jsonObj["template_url"] != null) - { - merged["templateUrl"] = jsonObj["template_url"].Value(); - } - - jsonObj = merged; - } - - using (StreamWriter fw = new StreamWriter(savePath)) - { - fw.Write(jsonObj.ToString(Formatting.Indented)); - Debug.LogMessage(LogEventLevel.Debug, "JSON config merged and saved to {0}", savePath); - } - + fw.Write(jsonObj.ToString(Formatting.Indented)); + Debug.LogMessage(LogEventLevel.Debug, "JSON config merged and saved to {0}", savePath); } - } - catch (Exception e) - { - Debug.LogMessage(e, "ERROR: Config load failed"); + } } - - /// - /// - /// - /// - /// - public static JObject MergeConfigs(JObject doubleConfig) + catch (Exception e) { - var system = JObject.FromObject(doubleConfig["system"]); - var template = JObject.FromObject(doubleConfig["template"]); - var merged = new JObject(); - - // Put together top-level objects - if (system["info"] != null) - merged.Add("info", Merge(template["info"], system["info"], "infO")); - else - merged.Add("info", template["info"]); - - merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray, - system["devices"] as JArray, "key", "devices")); - - if (system["rooms"] == null) - merged.Add("rooms", template["rooms"]); - else - merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray, - system["rooms"] as JArray, "key", "rooms")); - - if (system["sourceLists"] == null) - merged.Add("sourceLists", template["sourceLists"]); - else - merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"], "sourceLists")); - - if (system["destinationLists"] == null) - merged.Add("destinationLists", template["destinationLists"]); - else - merged.Add("destinationLists", - Merge(template["destinationLists"], system["destinationLists"], "destinationLists")); - - - if (system["cameraLists"] == null) - merged.Add("cameraLists", template["cameraLists"]); - else - merged.Add("cameraLists", Merge(template["cameraLists"], system["cameraLists"], "cameraLists")); - - if (system["audioControlPointLists"] == null) - merged.Add("audioControlPointLists", template["audioControlPointLists"]); - else - merged.Add("audioControlPointLists", - Merge(template["audioControlPointLists"], system["audioControlPointLists"], "audioControlPointLists")); - - - // Template tie lines take precedence. Config tool doesn't do them at system - // level anyway... - if (template["tieLines"] != null) - merged.Add("tieLines", template["tieLines"]); - else if (system["tieLines"] != null) - merged.Add("tieLines", system["tieLines"]); - else - merged.Add("tieLines", new JArray()); - - if (template["joinMaps"] != null) - merged.Add("joinMaps", template["joinMaps"]); - else - merged.Add("joinMaps", new JObject()); - - if (system["global"] != null) - merged.Add("global", Merge(template["global"], system["global"], "global")); - else - merged.Add("global", template["global"]); - - //Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged); - return merged; + Debug.LogMessage(e, "ERROR: Config load failed"); } + } - /// - /// Merges the contents of a base and a delta array, matching the entries on a top-level property - /// given by propertyName. Returns a merge of them. Items in the delta array that do not have - /// a matched item in base array will not be merged. Non keyed system items will replace the template items. - /// - static JArray MergeArraysOnTopLevelProperty(JArray a1, JArray a2, string propertyName, string path) + /// + /// + /// + /// + /// + public static JObject MergeConfigs(JObject doubleConfig) + { + var system = JObject.FromObject(doubleConfig["system"]); + var template = JObject.FromObject(doubleConfig["template"]); + var merged = new JObject(); + + // Put together top-level objects + if (system["info"] != null) + merged.Add("info", Merge(template["info"], system["info"], "infO")); + else + merged.Add("info", template["info"]); + + merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray, + system["devices"] as JArray, "key", "devices")); + + if (system["rooms"] == null) + merged.Add("rooms", template["rooms"]); + else + merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray, + system["rooms"] as JArray, "key", "rooms")); + + if (system["sourceLists"] == null) + merged.Add("sourceLists", template["sourceLists"]); + else + merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"], "sourceLists")); + + if (system["destinationLists"] == null) + merged.Add("destinationLists", template["destinationLists"]); + else + merged.Add("destinationLists", + Merge(template["destinationLists"], system["destinationLists"], "destinationLists")); + + + if (system["cameraLists"] == null) + merged.Add("cameraLists", template["cameraLists"]); + else + merged.Add("cameraLists", Merge(template["cameraLists"], system["cameraLists"], "cameraLists")); + + if (system["audioControlPointLists"] == null) + merged.Add("audioControlPointLists", template["audioControlPointLists"]); + else + merged.Add("audioControlPointLists", + Merge(template["audioControlPointLists"], system["audioControlPointLists"], "audioControlPointLists")); + + + // Template tie lines take precedence. Config tool doesn't do them at system + // level anyway... + if (template["tieLines"] != null) + merged.Add("tieLines", template["tieLines"]); + else if (system["tieLines"] != null) + merged.Add("tieLines", system["tieLines"]); + else + merged.Add("tieLines", new JArray()); + + if (template["joinMaps"] != null) + merged.Add("joinMaps", template["joinMaps"]); + else + merged.Add("joinMaps", new JObject()); + + if (system["global"] != null) + merged.Add("global", Merge(template["global"], system["global"], "global")); + else + merged.Add("global", template["global"]); + + //Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged); + return merged; + } + + /// + /// Merges the contents of a base and a delta array, matching the entries on a top-level property + /// given by propertyName. Returns a merge of them. Items in the delta array that do not have + /// a matched item in base array will not be merged. Non keyed system items will replace the template items. + /// + static JArray MergeArraysOnTopLevelProperty(JArray a1, JArray a2, string propertyName, string path) + { + var result = new JArray(); + if (a2 == null || a2.Count == 0) // If the system array is null or empty, return the template array + return a1; + else if (a1 != null) { - var result = new JArray(); - if (a2 == null || a2.Count == 0) // If the system array is null or empty, return the template array - return a1; - else if (a1 != null) - { - if (a2[0]["key"] == null) // If the first item in the system array has no key, overwrite the template array - { // with the system array - return a2; - } - else // The arrays are keyed, merge them by key - { - for (int i = 0; i < a1.Count(); i++) - { - var a1Dev = a1[i]; - // Try to get a system device and if found, merge it onto template - var a2Match = a2.FirstOrDefault(t => t[propertyName].Equals(a1Dev[propertyName]));// t.Value("uid") == tmplDev.Value("uid")); - if (a2Match != null) - { - var mergedItem = Merge(a1Dev, a2Match, string.Format("{0}[{1}].", path, i));// Merge(JObject.FromObject(a1Dev), JObject.FromObject(a2Match)); - result.Add(mergedItem); - } - else - result.Add(a1Dev); - } - } + if (a2[0]["key"] == null) // If the first item in the system array has no key, overwrite the template array + { // with the system array + return a2; } - return result; - } - - - /// - /// Helper for using with JTokens. Converts to JObject - /// - static JObject Merge(JToken t1, JToken t2, string path) - { - return Merge(JObject.FromObject(t1), JObject.FromObject(t2), path); - } - - /// - /// Merge o2 onto o1 - /// - /// - /// - /// - static JObject Merge(JObject o1, JObject o2, string path) - { - foreach (var o2Prop in o2) + else // The arrays are keyed, merge them by key { - var propKey = o2Prop.Key; - var o1Value = o1[propKey]; - var o2Value = o2[propKey]; - - // if the property doesn't exist on o1, then add it. - if (o1Value == null) + for (int i = 0; i < a1.Count(); i++) { - o1.Add(propKey, o2Value); - } - // otherwise merge them - else - { - // Drill down - var propPath = String.Format("{0}.{1}", path, propKey); - try + var a1Dev = a1[i]; + // Try to get a system device and if found, merge it onto template + var a2Match = a2.FirstOrDefault(t => t[propertyName].Equals(a1Dev[propertyName]));// t.Value("uid") == tmplDev.Value("uid")); + if (a2Match != null) { - - if (o1Value is JArray) - { - if (o2Value is JArray) - { - o1Value.Replace(MergeArraysOnTopLevelProperty(o1Value as JArray, o2Value as JArray, "key", propPath)); - } - } - else if (o2Prop.Value.HasValues && o1Value.HasValues) - { - o1Value.Replace(Merge(JObject.FromObject(o1Value), JObject.FromObject(o2Value), propPath)); - } - else - { - o1Value.Replace(o2Prop.Value); - } - } - catch (Exception e) - { - Debug.Console(1, Debug.ErrorLogLevel.Warning, "Cannot merge items at path {0}: \r{1}", propPath, e); + var mergedItem = Merge(a1Dev, a2Match, string.Format("{0}[{1}].", path, i));// Merge(JObject.FromObject(a1Dev), JObject.FromObject(a2Match)); + result.Add(mergedItem); } + else + result.Add(a1Dev); } } - return o1; } - } \ No newline at end of file + return result; + } + + + /// + /// Helper for using with JTokens. Converts to JObject + /// + static JObject Merge(JToken t1, JToken t2, string path) + { + return Merge(JObject.FromObject(t1), JObject.FromObject(t2), path); + } + + /// + /// Merge o2 onto o1 + /// + /// + /// + /// + static JObject Merge(JObject o1, JObject o2, string path) + { + foreach (var o2Prop in o2) + { + var propKey = o2Prop.Key; + var o1Value = o1[propKey]; + var o2Value = o2[propKey]; + + // if the property doesn't exist on o1, then add it. + if (o1Value == null) + { + o1.Add(propKey, o2Value); + } + // otherwise merge them + else + { + // Drill down + var propPath = String.Format("{0}.{1}", path, propKey); + try + { + + if (o1Value is JArray) + { + if (o2Value is JArray) + { + o1Value.Replace(MergeArraysOnTopLevelProperty(o1Value as JArray, o2Value as JArray, "key", propPath)); + } + } + else if (o2Prop.Value.HasValues && o1Value.HasValues) + { + o1Value.Replace(Merge(JObject.FromObject(o1Value), JObject.FromObject(o2Value), propPath)); + } + else + { + o1Value.Replace(o2Prop.Value); + } + } + catch (Exception e) + { + Debug.Console(1, Debug.ErrorLogLevel.Warning, "Cannot merge items at path {0}: \r{1}", propPath, e); + } + } + } + return o1; + } +} \ No newline at end of file diff --git a/src/PepperDash.Core/GenericRESTfulCommunications/GenericRESTfulClient.cs b/src/PepperDash.Core/GenericRESTfulCommunications/GenericRESTfulClient.cs index 86818f0c..f9096520 100644 --- a/src/PepperDash.Core/GenericRESTfulCommunications/GenericRESTfulClient.cs +++ b/src/PepperDash.Core/GenericRESTfulCommunications/GenericRESTfulClient.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; using Crestron.SimplSharp; -using static Crestron.SimplSharp.Net.Http; -using static Crestron.SimplSharp.Net.Https; +using Crestron.SimplSharp.Net.Http; +using Crestron.SimplSharp.Net.Https; namespace PepperDash.Core.GenericRESTfulCommunications; diff --git a/src/PepperDash.Core/JsonToSimpl/JsonToSimplFileMaster.cs b/src/PepperDash.Core/JsonToSimpl/JsonToSimplFileMaster.cs index 83c92e80..56652b7b 100644 --- a/src/PepperDash.Core/JsonToSimpl/JsonToSimplFileMaster.cs +++ b/src/PepperDash.Core/JsonToSimpl/JsonToSimplFileMaster.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Newtonsoft.Json.Linq; namespace PepperDash.Core.JsonToSimpl; @@ -79,7 +79,7 @@ public class JsonToSimplFileMaster : JsonToSimplMaster JsonToSimplConstants.DevicePlatformValueChange); // get the roomID - var roomId = Crestron.SimplSharp.InitialParametersClass.RoomId; + var roomId = Crestron.SimplSharp.InitialParametersClass.RoomId; if (!string.IsNullOrEmpty(roomId)) { OnStringChange(roomId, 0, JsonToSimplConstants.RoomIdChange); @@ -92,13 +92,13 @@ public class JsonToSimplFileMaster : JsonToSimplMaster OnStringChange(roomName, 0, JsonToSimplConstants.RoomNameChange); } - var rootDirectory = Directory.GetApplicationRootDirectory(); - OnStringChange(rootDirectory, 0, JsonToSimplConstants.RootDirectoryChange); - + var rootDirectory = Directory.GetCurrentDirectory(); + OnStringChange(rootDirectory, 0, JsonToSimplConstants.RootDirectoryChange); + var splusPath = string.Empty; if (Regex.IsMatch(filepath, @"user", RegexOptions.IgnoreCase)) { - if (is4Series) + if (is4Series) splusPath = Regex.Replace(filepath, "user", "user", RegexOptions.IgnoreCase); else if (isServer) splusPath = Regex.Replace(filepath, "user", "User", RegexOptions.IgnoreCase); @@ -107,10 +107,10 @@ public class JsonToSimplFileMaster : JsonToSimplMaster } filepath = splusPath.Replace(dirSeparatorAlt, dirSeparator); - + Filepath = string.Format("{1}{0}{2}", dirSeparator, rootDirectory, filepath.TrimStart(dirSeparator, dirSeparatorAlt)); - + OnStringChange(string.Format("Attempting to evaluate {0}", Filepath), 0, JsonToSimplConstants.StringValueChange); if (string.IsNullOrEmpty(Filepath)) @@ -130,10 +130,10 @@ public class JsonToSimplFileMaster : JsonToSimplMaster if (Directory.Exists(fileDirectory)) { // get the directory info - var directoryInfo = new DirectoryInfo(fileDirectory); + var directoryInfo = new DirectoryInfo(fileDirectory); // get the file to be read - var actualFile = directoryInfo.GetFiles(fileName).FirstOrDefault(); + var actualFile = directoryInfo.GetFiles(fileName).FirstOrDefault(); if (actualFile == null) { var msg = string.Format("JSON file not found: {0}", Filepath); @@ -145,7 +145,7 @@ public class JsonToSimplFileMaster : JsonToSimplMaster // \xSE\xR\PDT000-Template_Main_Config-Combined_DSP_v00.02.json // \USER\PDT000-Template_Main_Config-Combined_DSP_v00.02.json - ActualFilePath = actualFile.FullName; + ActualFilePath = actualFile.FullName; OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange); OnStringChange(string.Format("Actual JSON file is {0}", ActualFilePath), 0, JsonToSimplConstants.StringValueChange); Debug.Console(1, "Actual JSON file is {0}", ActualFilePath); @@ -159,9 +159,9 @@ public class JsonToSimplFileMaster : JsonToSimplMaster FilePathName = string.Format(@"{0}{1}", actualFile.DirectoryName, dirSeparator); OnStringChange(string.Format(@"{0}", actualFile.DirectoryName), 0, JsonToSimplConstants.FilePathResolvedChange); OnStringChange(string.Format(@"JSON File Path is {0}", actualFile.DirectoryName), 0, JsonToSimplConstants.StringValueChange); - Debug.Console(1, "JSON File Path is {0}", FilePathName); + Debug.Console(1, "JSON File Path is {0}", FilePathName); - var json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII); + var json = File.ReadAllText(ActualFilePath, System.Text.Encoding.ASCII); JsonObject = JObject.Parse(json); foreach (var child in Children) diff --git a/src/PepperDash.Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs b/src/PepperDash.Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs index 30c66dd2..57fe6011 100644 --- a/src/PepperDash.Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs +++ b/src/PepperDash.Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Newtonsoft.Json.Linq; using PepperDash.Core.Config; @@ -13,178 +13,178 @@ namespace PepperDash.Core.JsonToSimpl; /// public class JsonToSimplPortalFileMaster : JsonToSimplMaster { - /// - /// Sets the filepath as well as registers this with the Global.Masters list - /// - public string PortalFilepath { get; private set; } + /// + /// Sets the filepath as well as registers this with the Global.Masters list + /// + public string PortalFilepath { get; private set; } - /// - /// File path of the actual file being read (Portal or local) - /// - public string ActualFilePath { get; private set; } + /// + /// File path of the actual file being read (Portal or local) + /// + public string ActualFilePath { get; private set; } - /*****************************************************************************************/ - /** Privates **/ + /*****************************************************************************************/ + /** Privates **/ - // To prevent multiple same-file access - object StringBuilderLock = new object(); - static object FileLock = new object(); + // To prevent multiple same-file access + object StringBuilderLock = new object(); + static object FileLock = new object(); - /*****************************************************************************************/ + /*****************************************************************************************/ - /// - /// SIMPL+ default constructor. - /// - public JsonToSimplPortalFileMaster() - { + /// + /// SIMPL+ default constructor. + /// + public JsonToSimplPortalFileMaster() + { + } + + /// + /// Read, evaluate and udpate status + /// + public void EvaluateFile(string portalFilepath) + { + PortalFilepath = portalFilepath; + + OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange); + if (string.IsNullOrEmpty(PortalFilepath)) + { + CrestronConsole.PrintLine("Cannot evaluate file. JSON file path not set"); + return; } - /// - /// Read, evaluate and udpate status - /// - public void EvaluateFile(string portalFilepath) + // Resolve possible wildcarded filename + + // If the portal file is xyz.json, then + // the file we want to check for first will be called xyz.local.json + var localFilepath = Path.ChangeExtension(PortalFilepath, "local.json"); + Debug.Console(0, this, "Checking for local file {0}", localFilepath); + var actualLocalFile = GetActualFileInfoFromPath(localFilepath); + + if (actualLocalFile != null) { - PortalFilepath = portalFilepath; - - OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange); - if (string.IsNullOrEmpty(PortalFilepath)) + ActualFilePath = actualLocalFile.FullName; + OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange); + } + // If the local file does not exist, then read the portal file xyz.json + // and create the local. + else + { + Debug.Console(1, this, "Local JSON file not found {0}\rLoading portal JSON file", localFilepath); + var actualPortalFile = GetActualFileInfoFromPath(portalFilepath); + if (actualPortalFile != null) { - CrestronConsole.PrintLine("Cannot evaluate file. JSON file path not set"); - return; + var newLocalPath = Path.ChangeExtension(actualPortalFile.FullName, "local.json"); + // got the portal file, hand off to the merge / save method + PortalConfigReader.ReadAndMergeFileIfNecessary(actualPortalFile.FullName, newLocalPath); + ActualFilePath = newLocalPath; + OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange); } - - // Resolve possible wildcarded filename - - // If the portal file is xyz.json, then - // the file we want to check for first will be called xyz.local.json - var localFilepath = Path.ChangeExtension(PortalFilepath, "local.json"); - Debug.Console(0, this, "Checking for local file {0}", localFilepath); - var actualLocalFile = GetActualFileInfoFromPath(localFilepath); - - if (actualLocalFile != null) - { - ActualFilePath = actualLocalFile.FullName; - OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange); - } - // If the local file does not exist, then read the portal file xyz.json - // and create the local. else { - Debug.Console(1, this, "Local JSON file not found {0}\rLoading portal JSON file", localFilepath); - var actualPortalFile = GetActualFileInfoFromPath(portalFilepath); - if (actualPortalFile != null) - { - var newLocalPath = Path.ChangeExtension(actualPortalFile.FullName, "local.json"); - // got the portal file, hand off to the merge / save method - PortalConfigReader.ReadAndMergeFileIfNecessary(actualPortalFile.FullName, newLocalPath); - ActualFilePath = newLocalPath; - OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange); - } - else - { - var msg = string.Format("Portal JSON file not found: {0}", PortalFilepath); - Debug.Console(1, this, msg); - ErrorLog.Error(msg); - return; - } - } - - // At this point we should have a local file. Do it. - Debug.Console(1, "Reading local JSON file {0}", ActualFilePath); - - string json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII); - - try - { - JsonObject = JObject.Parse(json); - foreach (var child in Children) - child.ProcessAll(); - OnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange); - } - catch (Exception e) - { - var msg = string.Format("JSON parsing failed:\r{0}", e); - CrestronConsole.PrintLine(msg); + var msg = string.Format("Portal JSON file not found: {0}", PortalFilepath); + Debug.Console(1, this, msg); ErrorLog.Error(msg); return; } } - /// - /// Returns the FileInfo object for a given path, with possible wildcards - /// - /// - /// - FileInfo GetActualFileInfoFromPath(string path) - { - var dir = Path.GetDirectoryName(path); - var localFilename = Path.GetFileName(path); - var directory = new DirectoryInfo(dir); - // search the directory for the file w/ wildcards - return directory.GetFiles(localFilename).FirstOrDefault(); - } + // At this point we should have a local file. Do it. + Debug.Console(1, "Reading local JSON file {0}", ActualFilePath); - /// - /// - /// - /// - public void setDebugLevel(uint level) - { - Debug.SetDebugLevel(level); - } + string json = File.ReadAllText(ActualFilePath, System.Text.Encoding.ASCII); - /// - /// - /// - public override void Save() + try { - // this code is duplicated in the other masters!!!!!!!!!!!!! - UnsavedValues = new Dictionary(); - // Make each child update their values into master object + JsonObject = JObject.Parse(json); foreach (var child in Children) - { - Debug.Console(1, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key); - child.UpdateInputsForMaster(); - } + child.ProcessAll(); + OnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange); + } + catch (Exception e) + { + var msg = string.Format("JSON parsing failed:\r{0}", e); + CrestronConsole.PrintLine(msg); + ErrorLog.Error(msg); + return; + } + } - if (UnsavedValues == null || UnsavedValues.Count == 0) + /// + /// Returns the FileInfo object for a given path, with possible wildcards + /// + /// + /// + FileInfo GetActualFileInfoFromPath(string path) + { + var dir = Path.GetDirectoryName(path); + var localFilename = Path.GetFileName(path); + var directory = new DirectoryInfo(dir); + // search the directory for the file w/ wildcards + return directory.GetFiles(localFilename).FirstOrDefault(); + } + + /// + /// + /// + /// + public void setDebugLevel(uint level) + { + Debug.SetDebugLevel(level); + } + + /// + /// + /// + public override void Save() + { + // this code is duplicated in the other masters!!!!!!!!!!!!! + UnsavedValues = new Dictionary(); + // Make each child update their values into master object + foreach (var child in Children) + { + Debug.Console(1, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key); + child.UpdateInputsForMaster(); + } + + if (UnsavedValues == null || UnsavedValues.Count == 0) + { + Debug.Console(1, "Master [{0}] No updated values to save. Skipping", UniqueID); + return; + } + lock (FileLock) + { + Debug.Console(1, "Saving"); + foreach (var path in UnsavedValues.Keys) { - Debug.Console(1, "Master [{0}] No updated values to save. Skipping", UniqueID); - return; - } - lock (FileLock) - { - Debug.Console(1, "Saving"); - foreach (var path in UnsavedValues.Keys) - { - var tokenToReplace = JsonObject.SelectToken(path); - if (tokenToReplace != null) - {// It's found - tokenToReplace.Replace(UnsavedValues[path]); - Debug.Console(1, "JSON Master[{0}] Updating '{1}'", UniqueID, path); - } - else // No token. Let's make one - { - //http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net - Debug.Console(1, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path); - - } + var tokenToReplace = JsonObject.SelectToken(path); + if (tokenToReplace != null) + {// It's found + tokenToReplace.Replace(UnsavedValues[path]); + Debug.Console(1, "JSON Master[{0}] Updating '{1}'", UniqueID, path); } - using (StreamWriter sw = new StreamWriter(ActualFilePath)) + else // No token. Let's make one { - try - { - sw.Write(JsonObject.ToString()); - sw.Flush(); - } - catch (Exception e) - { - string err = string.Format("Error writing JSON file:\r{0}", e); - Debug.Console(0, err); - ErrorLog.Warn(err); - return; - } + //http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net + Debug.Console(1, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path); + + } + } + using (StreamWriter sw = new StreamWriter(ActualFilePath)) + { + try + { + sw.Write(JsonObject.ToString()); + sw.Flush(); + } + catch (Exception e) + { + string err = string.Format("Error writing JSON file:\r{0}", e); + Debug.Console(0, err); + ErrorLog.Warn(err); + return; } } } } +} diff --git a/src/PepperDash.Core/Logging/Debug.cs b/src/PepperDash.Core/Logging/Debug.cs index beb14d67..da6e3d17 100644 --- a/src/PepperDash.Core/Logging/Debug.cs +++ b/src/PepperDash.Core/Logging/Debug.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Reflection; using System.Text.RegularExpressions; using Crestron.SimplSharp; using Crestron.SimplSharp.CrestronDataStore; -using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.CrestronLogger; using Newtonsoft.Json; using PepperDash.Core.Logging; @@ -142,8 +142,8 @@ public static class Debug _websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true)); var logFilePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? - $@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log" : - $@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}room{InitialParametersClass.RoomId}{Path.DirectorySeparatorChar}global-log.log"; + $@"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log" : + $@"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}room{InitialParametersClass.RoomId}{Path.DirectorySeparatorChar}global-log.log"; CrestronConsole.PrintLine($"Saving log files to {logFilePath}"); @@ -1004,7 +1004,7 @@ public static class Debug return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber); } - return string.Format("{0}{1}user{1}debugSettings{1}{2}.json", Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId); + return string.Format("{0}{1}user{1}debugSettings{1}{2}.json", Directory.GetCurrentDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId); } /// diff --git a/src/PepperDash.Core/Logging/DebugContext.cs b/src/PepperDash.Core/Logging/DebugContext.cs index 8b8d5f2c..7ccb9bc6 100644 --- a/src/PepperDash.Core/Logging/DebugContext.cs +++ b/src/PepperDash.Core/Logging/DebugContext.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Newtonsoft.Json; diff --git a/src/PepperDash.Core/Web/RequestHandlers/DefaultRequestHandler.cs b/src/PepperDash.Core/Web/RequestHandlers/DefaultRequestHandler.cs index 2fee0a62..0429026d 100644 --- a/src/PepperDash.Core/Web/RequestHandlers/DefaultRequestHandler.cs +++ b/src/PepperDash.Core/Web/RequestHandlers/DefaultRequestHandler.cs @@ -2,15 +2,15 @@ namespace PepperDash.Core.Web.RequestHandlers; +/// +/// Web API default request handler +/// +public class DefaultRequestHandler : WebApiBaseRequestHandler +{ /// - /// Web API default request handler + /// Constructor /// - public class DefaultRequestHandler : WebApiBaseRequestHandler - { - /// - /// Constructor - /// - public DefaultRequestHandler() - : base(true) - { } - } \ No newline at end of file + public DefaultRequestHandler() + : base(true) + { } +} \ No newline at end of file diff --git a/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs b/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs index e8b3e85b..57840279 100644 --- a/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs +++ b/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestAsyncHandler.cs @@ -1,11 +1,11 @@ -using Crestron.SimplSharp.WebScripting; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using Crestron.SimplSharp.WebScripting; namespace PepperDash.Core.Web.RequestHandlers; -public abstract class WebApiBaseRequestAsyncHandler:IHttpCwsHandler +public abstract class WebApiBaseRequestAsyncHandler : IHttpCwsHandler { private readonly Dictionary> _handlers; protected readonly bool EnableCors; @@ -47,7 +47,7 @@ public abstract class WebApiBaseRequestAsyncHandler:IHttpCwsHandler { context.Response.StatusCode = 501; context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); + context.Response.End(); } /// diff --git a/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs b/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs index 22d368a5..235668ea 100644 --- a/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs +++ b/src/PepperDash.Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs @@ -4,22 +4,22 @@ using Crestron.SimplSharp.WebScripting; namespace PepperDash.Core.Web.RequestHandlers; +/// +/// CWS Base Handler, implements IHttpCwsHandler +/// +public abstract class WebApiBaseRequestHandler : IHttpCwsHandler +{ + private readonly Dictionary> _handlers; + protected readonly bool EnableCors; + /// - /// CWS Base Handler, implements IHttpCwsHandler + /// Constructor /// - public abstract class WebApiBaseRequestHandler : IHttpCwsHandler + protected WebApiBaseRequestHandler(bool enableCors) { - private readonly Dictionary> _handlers; - protected readonly bool EnableCors; + EnableCors = enableCors; - /// - /// Constructor - /// - protected WebApiBaseRequestHandler(bool enableCors) - { - EnableCors = enableCors; - - _handlers = new Dictionary> + _handlers = new Dictionary> { {"CONNECT", HandleConnect}, {"DELETE", HandleDelete}, @@ -31,134 +31,134 @@ namespace PepperDash.Core.Web.RequestHandlers; {"PUT", HandlePut}, {"TRACE", HandleTrace} }; + } + + /// + /// Constructor + /// + protected WebApiBaseRequestHandler() + : this(false) + { + } + + /// + /// Handles CONNECT method requests + /// + /// + protected virtual void HandleConnect(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles DELETE method requests + /// + /// + protected virtual void HandleDelete(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles GET method requests + /// + /// + protected virtual void HandleGet(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles HEAD method requests + /// + /// + protected virtual void HandleHead(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles OPTIONS method requests + /// + /// + protected virtual void HandleOptions(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles PATCH method requests + /// + /// + protected virtual void HandlePatch(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles POST method requests + /// + /// + protected virtual void HandlePost(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles PUT method requests + /// + /// + protected virtual void HandlePut(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles TRACE method requests + /// + /// + protected virtual void HandleTrace(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Process request + /// + /// + public void ProcessRequest(HttpCwsContext context) + { + Action handler; + + if (!_handlers.TryGetValue(context.Request.HttpMethod, out handler)) + { + return; } - /// - /// Constructor - /// - protected WebApiBaseRequestHandler() - : this(false) + if (EnableCors) { - } - - /// - /// Handles CONNECT method requests - /// - /// - protected virtual void HandleConnect(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); + context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); + context.Response.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); } - /// - /// Handles DELETE method requests - /// - /// - protected virtual void HandleDelete(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles GET method requests - /// - /// - protected virtual void HandleGet(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles HEAD method requests - /// - /// - protected virtual void HandleHead(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles OPTIONS method requests - /// - /// - protected virtual void HandleOptions(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles PATCH method requests - /// - /// - protected virtual void HandlePatch(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles POST method requests - /// - /// - protected virtual void HandlePost(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles PUT method requests - /// - /// - protected virtual void HandlePut(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Handles TRACE method requests - /// - /// - protected virtual void HandleTrace(HttpCwsContext context) - { - context.Response.StatusCode = 501; - context.Response.StatusDescription = "Not Implemented"; - context.Response.End(); - } - - /// - /// Process request - /// - /// - public void ProcessRequest(HttpCwsContext context) - { - Action handler; - - if (!_handlers.TryGetValue(context.Request.HttpMethod, out handler)) - { - return; - } - - if (EnableCors) - { - context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); - context.Response.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); - } - - handler(context); - } - } \ No newline at end of file + handler(context); + } +} \ No newline at end of file diff --git a/src/PepperDash.Core/Web/WebApiServer.cs b/src/PepperDash.Core/Web/WebApiServer.cs index dd6ed8d7..bcae771c 100644 --- a/src/PepperDash.Core/Web/WebApiServer.cs +++ b/src/PepperDash.Core/Web/WebApiServer.cs @@ -9,275 +9,275 @@ using PepperDash.Core.Web.RequestHandlers; namespace PepperDash.Core.Web; +/// +/// Web API server +/// +public class WebApiServer : IKeyName +{ + private const string SplusKey = "Uninitialized Web API Server"; + private const string DefaultName = "Web API Server"; + private const string DefaultBasePath = "/api"; + + private const uint DebugTrace = 0; + private const uint DebugInfo = 1; + private const uint DebugVerbose = 2; + + private readonly CCriticalSection _serverLock = new CCriticalSection(); + private HttpCwsServer _server; + /// - /// Web API server + /// Web API server key /// - public class WebApiServer : IKeyName + public string Key { get; private set; } + + /// + /// Web API server name + /// + public string Name { get; private set; } + + /// + /// CWS base path, will default to "/api" if not set via initialize method + /// + public string BasePath { get; private set; } + + /// + /// Indicates CWS is registered with base path + /// + public bool IsRegistered { get; private set; } + + /// + /// Http request handler + /// + //public IHttpCwsHandler HttpRequestHandler + //{ + // get { return _server.HttpRequestHandler; } + // set + // { + // if (_server == null) return; + // _server.HttpRequestHandler = value; + // } + //} + + /// + /// Received request event handler + /// + //public event EventHandler ReceivedRequestEvent + //{ + // add { _server.ReceivedRequestEvent += new HttpCwsRequestEventHandler(value); } + // remove { _server.ReceivedRequestEvent -= new HttpCwsRequestEventHandler(value); } + //} + + /// + /// Constructor for S+. Make sure to set necessary properties using init method + /// + public WebApiServer() + : this(SplusKey, DefaultName, null) { - private const string SplusKey = "Uninitialized Web API Server"; - private const string DefaultName = "Web API Server"; - private const string DefaultBasePath = "/api"; + } - private const uint DebugTrace = 0; - private const uint DebugInfo = 1; - private const uint DebugVerbose = 2; + /// + /// Constructor + /// + /// + /// + public WebApiServer(string key, string basePath) + : this(key, DefaultName, basePath) + { + } - private readonly CCriticalSection _serverLock = new CCriticalSection(); - private HttpCwsServer _server; + /// + /// Constructor + /// + /// + /// + /// + public WebApiServer(string key, string name, string basePath) + { + Key = key; + Name = string.IsNullOrEmpty(name) ? DefaultName : name; + BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; - /// - /// Web API server key - /// - public string Key { get; private set; } + if (_server == null) _server = new HttpCwsServer(BasePath); - /// - /// Web API server name - /// - public string Name { get; private set; } + _server.setProcessName(Key); + _server.HttpRequestHandler = new DefaultRequestHandler(); - /// - /// CWS base path, will default to "/api" if not set via initialize method - /// - public string BasePath { get; private set; } + CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; + CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; + } - /// - /// Indicates CWS is registered with base path - /// - public bool IsRegistered { get; private set; } + /// + /// Program status event handler + /// + /// + void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType != eProgramStatusEventType.Stopping) return; - /// - /// Http request handler - /// - //public IHttpCwsHandler HttpRequestHandler - //{ - // get { return _server.HttpRequestHandler; } - // set - // { - // if (_server == null) return; - // _server.HttpRequestHandler = value; - // } - //} + Debug.Console(DebugInfo, this, "Program stopping. stopping server"); - /// - /// Received request event handler - /// - //public event EventHandler ReceivedRequestEvent - //{ - // add { _server.ReceivedRequestEvent += new HttpCwsRequestEventHandler(value); } - // remove { _server.ReceivedRequestEvent -= new HttpCwsRequestEventHandler(value); } - //} + Stop(); + } - /// - /// Constructor for S+. Make sure to set necessary properties using init method - /// - public WebApiServer() - : this(SplusKey, DefaultName, null) + /// + /// Ethernet event handler + /// + /// + void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) + { + // Re-enable the server if the link comes back up and the status should be connected + if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp && IsRegistered) { + Debug.Console(DebugInfo, this, "Ethernet link up. Server is alreedy registered."); + return; } - /// - /// Constructor - /// - /// - /// - public WebApiServer(string key, string basePath) - : this(key, DefaultName, basePath) + Debug.Console(DebugInfo, this, "Ethernet link up. Starting server"); + + Start(); + } + + /// + /// Initializes CWS class + /// + public void Initialize(string key, string basePath) + { + Key = key; + BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; + } + + /// + /// Adds a route to CWS + /// + public void AddRoute(HttpCwsRoute route) + { + if (route == null) { + Debug.Console(DebugInfo, this, "Failed to add route, route parameter is null"); + return; } - /// - /// Constructor - /// - /// - /// - /// - public WebApiServer(string key, string name, string basePath) + _server.Routes.Add(route); + + } + + /// + /// Removes a route from CWS + /// + /// + public void RemoveRoute(HttpCwsRoute route) + { + if (route == null) { - Key = key; - Name = string.IsNullOrEmpty(name) ? DefaultName : name; - BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; - - if (_server == null) _server = new HttpCwsServer(BasePath); - - _server.setProcessName(Key); - _server.HttpRequestHandler = new DefaultRequestHandler(); - - CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; - CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; + Debug.Console(DebugInfo, this, "Failed to remote route, orute parameter is null"); + return; } - /// - /// Program status event handler - /// - /// - void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + _server.Routes.Remove(route); + } + + /// + /// Returns a list of the current routes + /// + public HttpCwsRouteCollection GetRouteCollection() + { + return _server.Routes; + } + + /// + /// Starts CWS instance + /// + public void Start() + { + try { - if (programEventType != eProgramStatusEventType.Stopping) return; + _serverLock.Enter(); - Debug.Console(DebugInfo, this, "Program stopping. stopping server"); - - Stop(); - } - - /// - /// Ethernet event handler - /// - /// - void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) - { - // Re-enable the server if the link comes back up and the status should be connected - if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp && IsRegistered) + if (_server == null) { - Debug.Console(DebugInfo, this, "Ethernet link up. Server is alreedy registered."); + Debug.Console(DebugInfo, this, "Server is null, unable to start"); return; } - Debug.Console(DebugInfo, this, "Ethernet link up. Starting server"); - - Start(); - } - - /// - /// Initializes CWS class - /// - public void Initialize(string key, string basePath) - { - Key = key; - BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; - } - - /// - /// Adds a route to CWS - /// - public void AddRoute(HttpCwsRoute route) - { - if (route == null) + if (IsRegistered) { - Debug.Console(DebugInfo, this, "Failed to add route, route parameter is null"); + Debug.Console(DebugInfo, this, "Server has already been started"); return; } - _server.Routes.Add(route); + IsRegistered = _server.Register(); + Debug.Console(DebugInfo, this, "Starting server, registration {0}", IsRegistered ? "was successful" : "failed"); } - - /// - /// Removes a route from CWS - /// - /// - public void RemoveRoute(HttpCwsRoute route) + catch (Exception ex) { - if (route == null) + Debug.Console(DebugInfo, this, "Start Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "Start Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "Start Exception InnerException: {0}", ex.InnerException); + } + finally + { + _serverLock.Leave(); + } + } + + /// + /// Stop CWS instance + /// + public void Stop() + { + try + { + _serverLock.Enter(); + + if (_server == null) { - Debug.Console(DebugInfo, this, "Failed to remote route, orute parameter is null"); + Debug.Console(DebugInfo, this, "Server is null or has already been stopped"); return; } - _server.Routes.Remove(route); - } + IsRegistered = _server.Unregister() == false; - /// - /// Returns a list of the current routes - /// - public HttpCwsRouteCollection GetRouteCollection() + Debug.Console(DebugInfo, this, "Stopping server, unregistration {0}", IsRegistered ? "failed" : "was successful"); + + _server.Dispose(); + _server = null; + } + catch (Exception ex) { - return _server.Routes; + Debug.Console(DebugInfo, this, "Server Stop Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "Server Stop Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "Server Stop Exception InnerException: {0}", ex.InnerException); } - - /// - /// Starts CWS instance - /// - public void Start() + finally { - try - { - _serverLock.Enter(); - - if (_server == null) - { - Debug.Console(DebugInfo, this, "Server is null, unable to start"); - return; - } - - if (IsRegistered) - { - Debug.Console(DebugInfo, this, "Server has already been started"); - return; - } - - IsRegistered = _server.Register(); - - Debug.Console(DebugInfo, this, "Starting server, registration {0}", IsRegistered ? "was successful" : "failed"); - } - catch (Exception ex) - { - Debug.Console(DebugInfo, this, "Start Exception Message: {0}", ex.Message); - Debug.Console(DebugVerbose, this, "Start Exception StackTrace: {0}", ex.StackTrace); - if (ex.InnerException != null) - Debug.Console(DebugVerbose, this, "Start Exception InnerException: {0}", ex.InnerException); - } - finally - { - _serverLock.Leave(); - } + _serverLock.Leave(); } + } - /// - /// Stop CWS instance - /// - public void Stop() + /// + /// Received request handler + /// + /// + /// This is here for development and testing + /// + /// + /// + public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args) + { + try { - try - { - _serverLock.Enter(); - - if (_server == null) - { - Debug.Console(DebugInfo, this, "Server is null or has already been stopped"); - return; - } - - IsRegistered = _server.Unregister() == false; - - Debug.Console(DebugInfo, this, "Stopping server, unregistration {0}", IsRegistered ? "failed" : "was successful"); - - _server.Dispose(); - _server = null; - } - catch (Exception ex) - { - Debug.Console(DebugInfo, this, "Server Stop Exception Message: {0}", ex.Message); - Debug.Console(DebugVerbose, this, "Server Stop Exception StackTrace: {0}", ex.StackTrace); - if (ex.InnerException != null) - Debug.Console(DebugVerbose, this, "Server Stop Exception InnerException: {0}", ex.InnerException); - } - finally - { - _serverLock.Leave(); - } + var j = JsonConvert.SerializeObject(args.Context, Formatting.Indented); + Debug.Console(DebugVerbose, this, "RecieveRequestEventHandler Context:\x0d\x0a{0}", j); } - - /// - /// Received request handler - /// - /// - /// This is here for development and testing - /// - /// - /// - public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args) + catch (Exception ex) { - try - { - var j = JsonConvert.SerializeObject(args.Context, Formatting.Indented); - Debug.Console(DebugVerbose, this, "RecieveRequestEventHandler Context:\x0d\x0a{0}", j); - } - catch (Exception ex) - { - Debug.Console(DebugInfo, this, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message); - Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception StackTrace: {0}", ex.StackTrace); - if (ex.InnerException != null) - Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception InnerException: {0}", ex.InnerException); - } + Debug.Console(DebugInfo, this, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception InnerException: {0}", ex.InnerException); } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs b/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs index e2c7d053..1b0c76b1 100644 --- a/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs +++ b/src/PepperDash.Core/WebApi/Presets/WebApiPasscodeClient.cs @@ -1,11 +1,11 @@ using System; +using System.IO; using Crestron.SimplSharp; // For Basic SIMPL# Classes -using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.Net.Http; +using Crestron.SimplSharp.Net.Https; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core.JsonToSimpl; -using static Crestron.SimplSharp.Net.Http; -using static Crestron.SimplSharp.Net.Https; namespace PepperDash.Core.WebApi.Presets; diff --git a/src/PepperDash.Core/XSigUtility/XSigHelpers.cs b/src/PepperDash.Core/XSigUtility/XSigHelpers.cs index 4f970be2..ff2da50e 100644 --- a/src/PepperDash.Core/XSigUtility/XSigHelpers.cs +++ b/src/PepperDash.Core/XSigUtility/XSigHelpers.cs @@ -1,6 +1,6 @@ using System; +using System.IO; using System.Linq; -using Crestron.SimplSharp.CrestronIO; using PepperDash.Core.Intersystem.Serialization; using PepperDash.Core.Intersystem.Tokens; diff --git a/src/PepperDash.Core/XSigUtility/XSigTokenStreamReader.cs b/src/PepperDash.Core/XSigUtility/XSigTokenStreamReader.cs index 3c222960..90787951 100644 --- a/src/PepperDash.Core/XSigUtility/XSigTokenStreamReader.cs +++ b/src/PepperDash.Core/XSigUtility/XSigTokenStreamReader.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Crestron.SimplSharp.CrestronIO; +using System.IO; using PepperDash.Core.Intersystem.Serialization; using PepperDash.Core.Intersystem.Tokens; @@ -82,7 +82,7 @@ public sealed class XSigTokenStreamReader : IDisposable { if (ch == -1) // Reached end of stream without end of data marker return null; - + chars[n++] = (char)ch; } diff --git a/src/PepperDash.Core/XSigUtility/XSigTokenStreamWriter.cs b/src/PepperDash.Core/XSigUtility/XSigTokenStreamWriter.cs index da973acd..d1b7e63a 100644 --- a/src/PepperDash.Core/XSigUtility/XSigTokenStreamWriter.cs +++ b/src/PepperDash.Core/XSigUtility/XSigTokenStreamWriter.cs @@ -1,7 +1,7 @@ using System; -using System.Linq; using System.Collections.Generic; -using Crestron.SimplSharp.CrestronIO; +using System.IO; +using System.Linq; using PepperDash.Core.Intersystem.Serialization; using PepperDash.Core.Intersystem.Tokens; diff --git a/src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs b/src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs index 52474d42..fef3abd7 100644 --- a/src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs +++ b/src/PepperDash.Essentials.Core/Comm and IR/IRPortHelper.cs @@ -2,10 +2,9 @@ using System; using System.Collections.Generic; +using System.IO; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharpPro; - using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; @@ -14,230 +13,230 @@ using Serilog.Events; namespace PepperDash.Essentials.Core; - /// - /// - /// - public static class IRPortHelper +/// +/// +/// +public static class IRPortHelper +{ + public static string IrDriverPathPrefix { - public static string IrDriverPathPrefix + get { - get - { - return Global.FilePathPrefix + "IR" + Global.DirectorySeparator; - } + return Global.FilePathPrefix + "IR" + Global.DirectorySeparator; } - - /// - /// Finds either the ControlSystem or a device controller that contains IR ports and - /// returns a port from the hardware device - /// - /// - /// IrPortConfig object. The port and or filename will be empty/null - /// if valid values don't exist on config - public static IrOutPortConfig GetIrPort(JToken propsToken) - { - var control = propsToken["control"]; - if (control == null) - return null; - if (control["method"].Value() != "ir") - { - Debug.LogMessage(LogEventLevel.Information, "IRPortHelper called with non-IR properties"); - return null; - } - - var port = new IrOutPortConfig(); - - var portDevKey = control.Value("controlPortDevKey"); - var portNum = control.Value("controlPortNumber"); - if (portDevKey == null || portNum == 0) - { - Debug.LogMessage(LogEventLevel.Debug, "WARNING: Properties is missing port device or port number"); - return port; - } - - IIROutputPorts irDev = null; - if (portDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase) - || portDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase)) - irDev = Global.ControlSystem; - else - irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts; - - if (irDev == null) - { - Debug.LogMessage(LogEventLevel.Debug, "[Config] Error, device with IR ports '{0}' not found", portDevKey); - return port; - } - - if (portNum <= irDev.NumberOfIROutputPorts) // success! - { - var file = IrDriverPathPrefix + control["irFile"].Value(); - port.Port = irDev.IROutputPorts[portNum]; - port.FileName = file; - return port; // new IrOutPortConfig { Port = irDev.IROutputPorts[portNum], FileName = file }; - } - else - { - Debug.LogMessage(LogEventLevel.Debug, "[Config] Error, device '{0}' IR port {1} out of range", - portDevKey, portNum); - return port; - } - } - - public static IROutputPort GetIrOutputPort(DeviceConfig dc) - { - var irControllerKey = dc.Key + "-ir"; - if (dc.Properties == null) - { - Debug.LogMessage(LogEventLevel.Information, "[{0}] WARNING: Device config does not include properties. IR will not function.", dc.Key); - return null; - } - - var control = dc.Properties["control"]; - if (control == null) - { - Debug.LogMessage(LogEventLevel.Information, - "WARNING: Device config does not include control properties. IR will not function for {0}", dc.Key); - return null; - } - - var portDevKey = control.Value("controlPortDevKey"); - var portNum = control.Value("controlPortNumber"); - IIROutputPorts irDev = null; - - if (portDevKey == null) - { - Debug.LogMessage(LogEventLevel.Information, "WARNING: control properties is missing ir device for {0}", dc.Key); - return null; - } - - if (portNum == 0) - { - Debug.LogMessage(LogEventLevel.Information, "WARNING: control properties is missing ir port number for {0}", dc.Key); - return null; - } - - if (portDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase) - || portDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase)) - irDev = Global.ControlSystem; - else - irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts; - - if (irDev == null) - { - Debug.LogMessage(LogEventLevel.Information, "WARNING: device with IR ports '{0}' not found", portDevKey); - return null; - } - if (portNum > irDev.NumberOfIROutputPorts) - { - Debug.LogMessage(LogEventLevel.Information, "WARNING: device '{0}' IR port {1} out of range", - portDevKey, portNum); - return null; - } - - - var port = irDev.IROutputPorts[portNum]; - - - - return port; - } - - public static IrOutputPortController GetIrOutputPortController(DeviceConfig config) - { - Debug.LogMessage(LogEventLevel.Debug, "Attempting to create new Ir Port Controller"); - - if (config == null) - { - return null; - } - - var postActivationFunc = new Func (GetIrOutputPort); - var irDevice = new IrOutputPortController(config.Key + "-ir", postActivationFunc, config); - - return irDevice; - } - - /* - /// - /// Returns a ready-to-go IrOutputPortController from a DeviceConfig object. - /// - public static IrOutputPortController GetIrOutputPortController(DeviceConfig devConf) - { - var irControllerKey = devConf.Key + "-ir"; - if (devConf.Properties == null) - { - Debug.LogMessage(LogEventLevel.Information, "[{0}] WARNING: Device config does not include properties. IR will not function.", devConf.Key); - return new IrOutputPortController(irControllerKey, null, ""); - } - - var control = devConf.Properties["control"]; - if (control == null) - { - var c = new IrOutputPortController(irControllerKey, null, ""); - Debug.LogMessage(LogEventLevel.Information, c, "WARNING: Device config does not include control properties. IR will not function"); - return c; - } - - var portDevKey = control.Value("controlPortDevKey"); - var portNum = control.Value("controlPortNumber"); - IIROutputPorts irDev = null; - - if (portDevKey == null) - { - var c = new IrOutputPortController(irControllerKey, null, ""); - Debug.LogMessage(LogEventLevel.Information, c, "WARNING: control properties is missing ir device"); - return c; - } - - if (portNum == 0) - { - var c = new IrOutputPortController(irControllerKey, null, ""); - Debug.LogMessage(LogEventLevel.Information, c, "WARNING: control properties is missing ir port number"); - return c; - } - - if (portDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase) - || portDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase)) - irDev = Global.ControlSystem; - else - irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts; - - if (irDev == null) - { - var c = new IrOutputPortController(irControllerKey, null, ""); - Debug.LogMessage(LogEventLevel.Information, c, "WARNING: device with IR ports '{0}' not found", portDevKey); - return c; - } - - if (portNum <= irDev.NumberOfIROutputPorts) // success! - return new IrOutputPortController(irControllerKey, irDev.IROutputPorts[portNum], - IrDriverPathPrefix + control["irFile"].Value()); - else - { - var c = new IrOutputPortController(irControllerKey, null, ""); - Debug.LogMessage(LogEventLevel.Information, c, "WARNING: device '{0}' IR port {1} out of range", - portDevKey, portNum); - return c; - } - }*/ } /// - /// Wrapper to help in IR port creation + /// Finds either the ControlSystem or a device controller that contains IR ports and + /// returns a port from the hardware device /// - public class IrOutPortConfig + /// + /// IrPortConfig object. The port and or filename will be empty/null + /// if valid values don't exist on config + public static IrOutPortConfig GetIrPort(JToken propsToken) { - [JsonProperty("port")] - public IROutputPort Port { get; set; } - - [JsonProperty("fileName")] - public string FileName { get; set; } - - [JsonProperty("useBridgeJoinMap")] - public bool UseBridgeJoinMap { get; set; } - - public IrOutPortConfig() + var control = propsToken["control"]; + if (control == null) + return null; + if (control["method"].Value() != "ir") { - FileName = ""; + Debug.LogMessage(LogEventLevel.Information, "IRPortHelper called with non-IR properties"); + return null; } - } \ No newline at end of file + + var port = new IrOutPortConfig(); + + var portDevKey = control.Value("controlPortDevKey"); + var portNum = control.Value("controlPortNumber"); + if (portDevKey == null || portNum == 0) + { + Debug.LogMessage(LogEventLevel.Debug, "WARNING: Properties is missing port device or port number"); + return port; + } + + IIROutputPorts irDev = null; + if (portDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase) + || portDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase)) + irDev = Global.ControlSystem; + else + irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts; + + if (irDev == null) + { + Debug.LogMessage(LogEventLevel.Debug, "[Config] Error, device with IR ports '{0}' not found", portDevKey); + return port; + } + + if (portNum <= irDev.NumberOfIROutputPorts) // success! + { + var file = IrDriverPathPrefix + control["irFile"].Value(); + port.Port = irDev.IROutputPorts[portNum]; + port.FileName = file; + return port; // new IrOutPortConfig { Port = irDev.IROutputPorts[portNum], FileName = file }; + } + else + { + Debug.LogMessage(LogEventLevel.Debug, "[Config] Error, device '{0}' IR port {1} out of range", + portDevKey, portNum); + return port; + } + } + + public static IROutputPort GetIrOutputPort(DeviceConfig dc) + { + var irControllerKey = dc.Key + "-ir"; + if (dc.Properties == null) + { + Debug.LogMessage(LogEventLevel.Information, "[{0}] WARNING: Device config does not include properties. IR will not function.", dc.Key); + return null; + } + + var control = dc.Properties["control"]; + if (control == null) + { + Debug.LogMessage(LogEventLevel.Information, + "WARNING: Device config does not include control properties. IR will not function for {0}", dc.Key); + return null; + } + + var portDevKey = control.Value("controlPortDevKey"); + var portNum = control.Value("controlPortNumber"); + IIROutputPorts irDev = null; + + if (portDevKey == null) + { + Debug.LogMessage(LogEventLevel.Information, "WARNING: control properties is missing ir device for {0}", dc.Key); + return null; + } + + if (portNum == 0) + { + Debug.LogMessage(LogEventLevel.Information, "WARNING: control properties is missing ir port number for {0}", dc.Key); + return null; + } + + if (portDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase) + || portDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase)) + irDev = Global.ControlSystem; + else + irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts; + + if (irDev == null) + { + Debug.LogMessage(LogEventLevel.Information, "WARNING: device with IR ports '{0}' not found", portDevKey); + return null; + } + if (portNum > irDev.NumberOfIROutputPorts) + { + Debug.LogMessage(LogEventLevel.Information, "WARNING: device '{0}' IR port {1} out of range", + portDevKey, portNum); + return null; + } + + + var port = irDev.IROutputPorts[portNum]; + + + + return port; + } + + public static IrOutputPortController GetIrOutputPortController(DeviceConfig config) + { + Debug.LogMessage(LogEventLevel.Debug, "Attempting to create new Ir Port Controller"); + + if (config == null) + { + return null; + } + + var postActivationFunc = new Func(GetIrOutputPort); + var irDevice = new IrOutputPortController(config.Key + "-ir", postActivationFunc, config); + + return irDevice; + } + + /* +/// +/// Returns a ready-to-go IrOutputPortController from a DeviceConfig object. +/// +public static IrOutputPortController GetIrOutputPortController(DeviceConfig devConf) +{ + var irControllerKey = devConf.Key + "-ir"; + if (devConf.Properties == null) + { + Debug.LogMessage(LogEventLevel.Information, "[{0}] WARNING: Device config does not include properties. IR will not function.", devConf.Key); + return new IrOutputPortController(irControllerKey, null, ""); + } + + var control = devConf.Properties["control"]; + if (control == null) + { + var c = new IrOutputPortController(irControllerKey, null, ""); + Debug.LogMessage(LogEventLevel.Information, c, "WARNING: Device config does not include control properties. IR will not function"); + return c; + } + + var portDevKey = control.Value("controlPortDevKey"); + var portNum = control.Value("controlPortNumber"); + IIROutputPorts irDev = null; + + if (portDevKey == null) + { + var c = new IrOutputPortController(irControllerKey, null, ""); + Debug.LogMessage(LogEventLevel.Information, c, "WARNING: control properties is missing ir device"); + return c; + } + + if (portNum == 0) + { + var c = new IrOutputPortController(irControllerKey, null, ""); + Debug.LogMessage(LogEventLevel.Information, c, "WARNING: control properties is missing ir port number"); + return c; + } + + if (portDevKey.Equals("controlSystem", StringComparison.OrdinalIgnoreCase) + || portDevKey.Equals("processor", StringComparison.OrdinalIgnoreCase)) + irDev = Global.ControlSystem; + else + irDev = DeviceManager.GetDeviceForKey(portDevKey) as IIROutputPorts; + + if (irDev == null) + { + var c = new IrOutputPortController(irControllerKey, null, ""); + Debug.LogMessage(LogEventLevel.Information, c, "WARNING: device with IR ports '{0}' not found", portDevKey); + return c; + } + + if (portNum <= irDev.NumberOfIROutputPorts) // success! + return new IrOutputPortController(irControllerKey, irDev.IROutputPorts[portNum], + IrDriverPathPrefix + control["irFile"].Value()); + else + { + var c = new IrOutputPortController(irControllerKey, null, ""); + Debug.LogMessage(LogEventLevel.Information, c, "WARNING: device '{0}' IR port {1} out of range", + portDevKey, portNum); + return c; + } +}*/ +} + +/// +/// Wrapper to help in IR port creation +/// +public class IrOutPortConfig +{ + [JsonProperty("port")] + public IROutputPort Port { get; set; } + + [JsonProperty("fileName")] + public string FileName { get; set; } + + [JsonProperty("useBridgeJoinMap")] + public bool UseBridgeJoinMap { get; set; } + + public IrOutPortConfig() + { + FileName = ""; + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Config/DeviceConfig.cs b/src/PepperDash.Essentials.Core/Config/DeviceConfig.cs index 49994abc..0830dd91 100644 --- a/src/PepperDash.Essentials.Core/Config/DeviceConfig.cs +++ b/src/PepperDash.Essentials.Core/Config/DeviceConfig.cs @@ -2,10 +2,9 @@ using System; using System.Collections.Generic; +using System.IO; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharpPro; - using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; @@ -47,7 +46,7 @@ public class DeviceConfig //Properties = JToken.FromObject(dc.Properties); } - public DeviceConfig() {} + public DeviceConfig() { } } /// diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs index d81223e3..e3104105 100644 --- a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs +++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs @@ -1,10 +1,10 @@  using System; +using System.IO; using System.Linq; using System.Text; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; @@ -13,24 +13,24 @@ using Serilog.Events; namespace PepperDash.Essentials.Core.Config; - /// - /// Loads the ConfigObject from the file - /// - public class ConfigReader - { - public const string LocalConfigPresent = - @" +/// +/// Loads the ConfigObject from the file +/// +public class ConfigReader +{ + public const string LocalConfigPresent = + @" *************************************************** ************* Using Local config file ************* ***************************************************"; - public static EssentialsConfig ConfigObject { get; private set; } + public static EssentialsConfig ConfigObject { get; private set; } - public static bool LoadConfig2() - { - Debug.LogMessage(LogEventLevel.Information, "Loading unmerged system/template portal configuration file."); - try - { + public static bool LoadConfig2() + { + Debug.LogMessage(LogEventLevel.Information, "Loading unmerged system/template portal configuration file."); + try + { // Check for local config file first var filePath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder + Global.DirectorySeparator + Global.ConfigFileName; @@ -50,10 +50,10 @@ namespace PepperDash.Essentials.Core.Config; "****Error: Multiple Local Configuration files present. Please ensure only a single file exists and reset program.****"); return false; } - if(configFiles.Length == 1) + if (configFiles.Length == 1) { localConfigFound = true; - + } } else @@ -64,7 +64,7 @@ namespace PepperDash.Essentials.Core.Config; } // Check for Portal Config - if(!localConfigFound) + if (!localConfigFound) { filePath = Global.FilePathPrefix + Global.ConfigFileName; @@ -104,10 +104,10 @@ namespace PepperDash.Essentials.Core.Config; filePath = configFiles[0].FullName; // Generate debug statement if using a local file. - if (localConfigFound) - { + if (localConfigFound) + { GetLocalFileMessage(filePath); - } + } // Read the file using (StreamReader fs = new StreamReader(filePath)) @@ -144,13 +144,13 @@ namespace PepperDash.Essentials.Core.Config; return true; } - } - catch (Exception e) - { + } + catch (Exception e) + { Debug.LogMessage(LogEventLevel.Information, "ERROR: Config load failed: \r{0}", e); - return false; - } - } + return false; + } + } /// /// Returns all the files from the directory specified. @@ -184,19 +184,19 @@ namespace PepperDash.Essentials.Core.Config; } } - /// - /// Returns the group for a given device key in config - /// - /// - /// + /// + /// Returns the group for a given device key in config + /// + /// + /// public static string GetGroupForDeviceKey(string key) { var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); return dev == null ? null : dev.Group; } - private static void GetLocalFileMessage(string filePath) - { + private static void GetLocalFileMessage(string filePath) + { var filePathLength = filePath.Length + 2; var debugStringWidth = filePathLength + 12; @@ -211,46 +211,46 @@ namespace PepperDash.Essentials.Core.Config; var bookend2 = (debugStringWidth - filePathLength) / 2; - var newDebugString = new StringBuilder() - .Append(CrestronEnvironment.NewLine) + var newDebugString = new StringBuilder() + .Append(CrestronEnvironment.NewLine) // Line 1 - .Append(new string('*', debugStringWidth)) - .Append(CrestronEnvironment.NewLine) + .Append(new string('*', debugStringWidth)) + .Append(CrestronEnvironment.NewLine) // Line 2 - .Append(new string('*', debugStringWidth)) - .Append(CrestronEnvironment.NewLine) + .Append(new string('*', debugStringWidth)) + .Append(CrestronEnvironment.NewLine) // Line 3 - .Append(new string('*', 2)) - .Append(new string(' ', debugStringWidth - 4)) - .Append(new string('*', 2)) - .Append(CrestronEnvironment.NewLine) + .Append(new string('*', 2)) + .Append(new string(' ', debugStringWidth - 4)) + .Append(new string('*', 2)) + .Append(CrestronEnvironment.NewLine) // Line 4 - .Append(new string('*', 2)) - .Append(new string(' ', bookend1 - 2)) - .Append(qualifier) - .Append(new string(' ', bookend1 - 2)) - .Append(new string('*', 2)) - .Append(CrestronEnvironment.NewLine) + .Append(new string('*', 2)) + .Append(new string(' ', bookend1 - 2)) + .Append(qualifier) + .Append(new string(' ', bookend1 - 2)) + .Append(new string('*', 2)) + .Append(CrestronEnvironment.NewLine) // Line 5 - .Append(new string('*', 2)) - .Append(new string(' ', bookend2 - 2)) - .Append(" " + filePath + " ") - .Append(new string(' ', bookend2 - 2)) - .Append(new string('*', 2)) - .Append(CrestronEnvironment.NewLine) + .Append(new string('*', 2)) + .Append(new string(' ', bookend2 - 2)) + .Append(" " + filePath + " ") + .Append(new string(' ', bookend2 - 2)) + .Append(new string('*', 2)) + .Append(CrestronEnvironment.NewLine) // Line 6 - .Append(new string('*', 2)) - .Append(new string(' ', debugStringWidth - 4)) - .Append(new string('*', 2)) - .Append(CrestronEnvironment.NewLine) + .Append(new string('*', 2)) + .Append(new string(' ', debugStringWidth - 4)) + .Append(new string('*', 2)) + .Append(CrestronEnvironment.NewLine) // Line 7 - .Append(new string('*', debugStringWidth)) - .Append(CrestronEnvironment.NewLine) + .Append(new string('*', debugStringWidth)) + .Append(CrestronEnvironment.NewLine) // Line 8 - .Append(new string('*', debugStringWidth)); + .Append(new string('*', debugStringWidth)); Debug.LogMessage(LogEventLevel.Verbose, "Found Local config file: '{0}'", filePath); Debug.LogMessage(LogEventLevel.Information, newDebugString.ToString()); - } + } - } \ No newline at end of file +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs index 0f14e455..7d6d56fb 100644 --- a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs +++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigUpdater.cs @@ -2,15 +2,14 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Crestron.SimplSharp.Net.Http; using Crestron.SimplSharpPro.Diagnostics; - +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using PepperDash.Core; using Serilog.Events; @@ -71,7 +70,7 @@ public static class ConfigUpdater { var handler = ConfigStatusChanged; - if(handler != null) + if (handler != null) { handler(typeof(ConfigUpdater), new ConfigStatusEventArgs(status)); } @@ -79,7 +78,7 @@ public static class ConfigUpdater static void WriteConfigToFile(string configData) { - var filePath = Global.FilePathPrefix+ "configurationFile-updated.json"; + var filePath = Global.FilePathPrefix + "configurationFile-updated.json"; try { @@ -94,7 +93,7 @@ public static class ConfigUpdater Debug.LogMessage(LogEventLevel.Debug, "Error parsing new config: {0}", e); OnStatusUpdate(eUpdateStatus.UpdateFailed); - } + } } /// @@ -132,18 +131,18 @@ public static class ConfigUpdater if (!Directory.Exists(archiveDirectoryPath)) { // Directory does not exist, create it - Directory.Create(archiveDirectoryPath); + Directory.CreateDirectory(archiveDirectoryPath); } else { // Directory exists, first clear any contents var archivedConfigFiles = ConfigReader.GetConfigFiles(archiveDirectoryPath + Global.DirectorySeparator + Global.ConfigFileName + ".bak"); - if(archivedConfigFiles != null || archivedConfigFiles.Length > 0) + if (archivedConfigFiles != null || archivedConfigFiles.Length > 0) { Debug.LogMessage(LogEventLevel.Information, "{0} Existing files found in archive folder. Deleting.", archivedConfigFiles.Length); - for (int i = 0; i < archivedConfigFiles.Length; i++ ) + for (int i = 0; i < archivedConfigFiles.Length; i++) { var file = archivedConfigFiles[i]; Debug.LogMessage(LogEventLevel.Information, "Deleting archived file: '{0}'", file.FullName); @@ -160,9 +159,9 @@ public static class ConfigUpdater // Moves the file and appends the .bak extension var fileDest = archiveDirectoryPath + "/" + file.Name + ".bak"; - if(!File.Exists(fileDest)) + if (!File.Exists(fileDest)) { - file.MoveTo(fileDest); + file.MoveTo(fileDest); } else Debug.LogMessage(LogEventLevel.Information, "Cannot move file to archive folder. Existing file already exists with same name: '{0}'", fileDest); @@ -197,12 +196,12 @@ public static class ConfigUpdater CrestronConsole.SendControlSystemCommand(string.Format("progreset -p:{0}", InitialParametersClass.ApplicationNumber), ref response); - Debug.LogMessage(LogEventLevel.Debug, "Console Response: {0}", response); + Debug.LogMessage(LogEventLevel.Debug, "Console Response: {0}", response); } } - public enum eUpdateStatus +public enum eUpdateStatus { UpdateStarted, ConfigFileReceived, diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigWriter.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigWriter.cs index a688b378..43f1a239 100644 --- a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigWriter.cs +++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigWriter.cs @@ -2,10 +2,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; @@ -23,7 +23,7 @@ public class ConfigWriter public const long WriteTimeout = 30000; public static CTimer WriteTimer; - static CCriticalSection fileLock = new CCriticalSection(); + static CCriticalSection fileLock = new CCriticalSection(); /// /// Updates the config properties of a device @@ -77,9 +77,9 @@ public class ConfigWriter { bool success = false; - var roomConfigIndex = ConfigReader.ConfigObject.Rooms.FindIndex(d => d.Key.Equals(config.Key)); + var roomConfigIndex = ConfigReader.ConfigObject.Rooms.FindIndex(d => d.Key.Equals(config.Key)); - if (roomConfigIndex >= 0) + if (roomConfigIndex >= 0) { ConfigReader.ConfigObject.Rooms[roomConfigIndex] = config; diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs index bab6ae1d..7a638723 100644 --- a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs +++ b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs @@ -2,88 +2,88 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text.RegularExpressions; - -using Crestron.SimplSharp.CrestronIO; using Newtonsoft.Json; - using PepperDash.Core; namespace PepperDash.Essentials.Core.Config; - /// - /// Loads the ConfigObject from the file - /// - public class EssentialsConfig : BasicConfig +/// +/// Loads the ConfigObject from the file +/// +public class EssentialsConfig : BasicConfig +{ + [JsonProperty("system_url")] + public string SystemUrl { get; set; } + + [JsonProperty("template_url")] + public string TemplateUrl { get; set; } + + + [JsonProperty("systemUuid")] + public string SystemUuid { - [JsonProperty("system_url")] - public string SystemUrl { get; set; } + get + { + if (string.IsNullOrEmpty(SystemUrl)) + return "missing url"; - [JsonProperty("template_url")] - public string TemplateUrl { get; set; } - - - [JsonProperty("systemUuid")] - public string SystemUuid - { - get - { - if (string.IsNullOrEmpty(SystemUrl)) - return "missing url"; - - if (SystemUrl.Contains("#")) - { - var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*"); - string uuid = result.Groups[1].Value; - return uuid; - } else - { - var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*"); - string uuid = result.Groups[1].Value; - return uuid; - } - } - } - - [JsonProperty("templateUuid")] - public string TemplateUuid - { - get - { - if (string.IsNullOrEmpty(TemplateUrl)) - return "missing template url"; - - if (TemplateUrl.Contains("#")) - { - var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*"); - string uuid = result.Groups[1].Value; - return uuid; - } else - { - var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*"); - string uuid = result.Groups[2].Value; - return uuid; - } - } - } - - [JsonProperty("rooms")] - public List Rooms { get; set; } - - - public EssentialsConfig() - : base() - { - Rooms = new List(); - } + if (SystemUrl.Contains("#")) + { + var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*"); + string uuid = result.Groups[1].Value; + return uuid; + } + else + { + var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*"); + string uuid = result.Groups[1].Value; + return uuid; + } + } } - - /// - /// - /// - public class SystemTemplateConfigs - { - public EssentialsConfig System { get; set; } - public EssentialsConfig Template { get; set; } - } \ No newline at end of file + [JsonProperty("templateUuid")] + public string TemplateUuid + { + get + { + if (string.IsNullOrEmpty(TemplateUrl)) + return "missing template url"; + + if (TemplateUrl.Contains("#")) + { + var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*"); + string uuid = result.Groups[1].Value; + return uuid; + } + else + { + var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*"); + string uuid = result.Groups[2].Value; + return uuid; + } + } + } + + [JsonProperty("rooms")] + public List Rooms { get; set; } + + + public EssentialsConfig() + : base() + { + Rooms = new List(); + } +} + +/// +/// +/// +public class SystemTemplateConfigs +{ + public EssentialsConfig System { get; set; } + + public EssentialsConfig Template { get; set; } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/File/FileIO.cs b/src/PepperDash.Essentials.Core/File/FileIO.cs index 98ad787f..144a0ead 100644 --- a/src/PepperDash.Essentials.Core/File/FileIO.cs +++ b/src/PepperDash.Essentials.Core/File/FileIO.cs @@ -1,276 +1,276 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using PepperDash.Core; using Crestron.SimplSharpPro.CrestronThread; +using PepperDash.Core; using Serilog.Events; namespace PepperDash.Essentials.Core; - public static class FileIO +public static class FileIO +{ + + static CCriticalSection fileLock = new CCriticalSection(); + public delegate void GotFileEventHandler(object sender, FileEventArgs e); + public static event GotFileEventHandler GotFileEvent; + + /// + /// Get the full file info from a path/filename, can include wildcards. + /// + /// + /// + public static FileInfo[] GetFiles(string fileName) { - - static CCriticalSection fileLock = new CCriticalSection(); - public delegate void GotFileEventHandler(object sender, FileEventArgs e); - public static event GotFileEventHandler GotFileEvent; - - /// - /// Get the full file info from a path/filename, can include wildcards. - /// - /// - /// - public static FileInfo[] GetFiles(string fileName) - { - string fullFilePath = Global.FilePathPrefix + fileName; - DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(fullFilePath)); - var files = dirInfo.GetFiles(Path.GetFileName(fullFilePath)); - Debug.LogMessage(LogEventLevel.Information, "FileIO found: {0}, {1}", files.Count(), fullFilePath); - if (files.Count() > 0) - { - return files; - } - else - { - return null; - } - } - - public static FileInfo GetFile(string fileName) - { - string fullFilePath = Global.FilePathPrefix + fileName; - DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(fullFilePath)); - var files = dirInfo.GetFiles(Path.GetFileName(fullFilePath)); - Debug.LogMessage(LogEventLevel.Information, "FileIO found: {0}, {1}", files.Count(), fullFilePath); - if (files.Count() > 0) - { - return files.FirstOrDefault(); - } - else - { - return null; - } - } - - - /// - /// Get the data from string path/filename - /// - /// - /// - public static string ReadDataFromFile(string fileName) + string fullFilePath = Global.FilePathPrefix + fileName; + DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(fullFilePath)); + var files = dirInfo.GetFiles(Path.GetFileName(fullFilePath)); + Debug.LogMessage(LogEventLevel.Information, "FileIO found: {0}, {1}", files.Count(), fullFilePath); + if (files.Count() > 0) { - try - { - return ReadDataFromFile(GetFile(fileName)); - } - catch (Exception e) - { - Debug.LogMessage(LogEventLevel.Information, "Error: FileIO read failed: \r{0}", e); - return ""; - } + return files; } - - /// - /// Get the data with fileInfo object - /// - /// - /// - public static string ReadDataFromFile(FileInfo file) + else { - try - { - if (fileLock.TryEnter()) - { - DirectoryInfo dirInfo = new DirectoryInfo(file.DirectoryName); - Debug.LogMessage(LogEventLevel.Verbose, "FileIO Getting Data {0}", file.FullName); + return null; + } + } - if (File.Exists(file.FullName)) + public static FileInfo GetFile(string fileName) + { + string fullFilePath = Global.FilePathPrefix + fileName; + DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(fullFilePath)); + var files = dirInfo.GetFiles(Path.GetFileName(fullFilePath)); + Debug.LogMessage(LogEventLevel.Information, "FileIO found: {0}, {1}", files.Count(), fullFilePath); + if (files.Count() > 0) + { + return files.FirstOrDefault(); + } + else + { + return null; + } + } + + + /// + /// Get the data from string path/filename + /// + /// + /// + public static string ReadDataFromFile(string fileName) + { + try + { + return ReadDataFromFile(GetFile(fileName)); + } + catch (Exception e) + { + Debug.LogMessage(LogEventLevel.Information, "Error: FileIO read failed: \r{0}", e); + return ""; + } + } + + /// + /// Get the data with fileInfo object + /// + /// + /// + public static string ReadDataFromFile(FileInfo file) + { + try + { + if (fileLock.TryEnter()) + { + DirectoryInfo dirInfo = new DirectoryInfo(file.DirectoryName); + Debug.LogMessage(LogEventLevel.Verbose, "FileIO Getting Data {0}", file.FullName); + + if (File.Exists(file.FullName)) + { + using (StreamReader r = new StreamReader(file.FullName)) { - using (StreamReader r = new StreamReader(file.FullName)) - { - return r.ReadToEnd(); - } - } - else - { - Debug.LogMessage(LogEventLevel.Verbose, "File {0} does not exsist", file.FullName); - return ""; + return r.ReadToEnd(); } } else { - Debug.LogMessage(LogEventLevel.Information, "FileIO Unable to enter FileLock"); + Debug.LogMessage(LogEventLevel.Verbose, "File {0} does not exsist", file.FullName); return ""; } - - } - catch (Exception e) - { - Debug.LogMessage(LogEventLevel.Information, "Error: FileIO read failed: \r{0}", e); - return ""; - } - finally - { - if (fileLock != null && !fileLock.Disposed) - fileLock.Leave(); - - } - } - - - public static void ReadDataFromFileASync(string fileName) - { - try - { - ReadDataFromFileASync(GetFile(fileName)); - } - catch (Exception e) - { - Debug.LogMessage(LogEventLevel.Information, "Error: FileIO read failed: \r{0}", e); - } - } - - public static void ReadDataFromFileASync(FileInfo file) - { - try - { - CrestronInvoke.BeginInvoke(o => _ReadDataFromFileASync(file)); - } - catch (Exception e) - { - Debug.LogMessage(LogEventLevel.Information, "Error: FileIO read failed: \r{0}", e); - } - } - - private static void _ReadDataFromFileASync(FileInfo file) - { - string data; - try - { - if (fileLock.TryEnter()) - { - DirectoryInfo dirInfo = new DirectoryInfo(file.Name); - Debug.LogMessage(LogEventLevel.Verbose, "FileIO Getting Data {0}", file.FullName); - - - if (File.Exists(file.FullName)) - { - using (StreamReader r = new StreamReader(file.FullName)) - { - data = r.ReadToEnd(); - } - } - else - { - Debug.LogMessage(LogEventLevel.Verbose, "File {0} Does not exsist", file.FullName); - data = ""; - } - GotFileEvent.Invoke(null, new FileEventArgs(data)); - } - else - { - Debug.LogMessage(LogEventLevel.Information, "FileIO Unable to enter FileLock"); - } - - } - catch (Exception e) - { - Debug.LogMessage(LogEventLevel.Information, "Error: FileIO read failed: \r{0}", e); - data = ""; - } - finally - { - if (fileLock != null && !fileLock.Disposed) - fileLock.Leave(); - - } - - - - } - - /// - /// - /// - /// - /// - public static void WriteDataToFile(string data, string filePath) - { - Thread _WriteFileThread; - _WriteFileThread = new Thread((O) => _WriteFileMethod(data, Global.FilePathPrefix + "/" + filePath), null, Thread.eThreadStartOptions.CreateSuspended); - _WriteFileThread.Priority = Thread.eThreadPriority.LowestPriority; - _WriteFileThread.Start(); - Debug.LogMessage(LogEventLevel.Information, "New WriteFile Thread"); - - } - - static object _WriteFileMethod(string data, string filePath) - { - Debug.LogMessage(LogEventLevel.Information, "Attempting to write file: '{0}'", filePath); - - try - { - if (fileLock.TryEnter()) - { - - using (StreamWriter sw = new StreamWriter(filePath)) - { - sw.Write(data); - sw.Flush(); - } - - } - else - { - Debug.LogMessage(LogEventLevel.Information, "FileIO Unable to enter FileLock"); - } - - } - catch (Exception e) - { - Debug.LogMessage(LogEventLevel.Error, "Error: FileIO write failed: \r{0}", e); - } - finally - { - if (fileLock != null && !fileLock.Disposed) - fileLock.Leave(); - - } - return null; - - } - - /// - /// - /// - /// - public static bool FileIoUnitTest() - { - var testData = "Testing FileIO"; - FileIO.WriteDataToFile(testData, "\\user\\FileIOTest.pdt"); - - var file = FileIO.GetFile("\\user\\*FileIOTest*"); - - var readData = FileIO.ReadDataFromFile(file); - Debug.LogMessage(LogEventLevel.Information, "Returned {0}", readData); - File.Delete(file.FullName); - if (testData == readData) - { - return true; } else { - return false; + Debug.LogMessage(LogEventLevel.Information, "FileIO Unable to enter FileLock"); + return ""; } + + } + catch (Exception e) + { + Debug.LogMessage(LogEventLevel.Information, "Error: FileIO read failed: \r{0}", e); + return ""; + } + finally + { + if (fileLock != null && !fileLock.Disposed) + fileLock.Leave(); + + } + } + + + public static void ReadDataFromFileASync(string fileName) + { + try + { + ReadDataFromFileASync(GetFile(fileName)); + } + catch (Exception e) + { + Debug.LogMessage(LogEventLevel.Information, "Error: FileIO read failed: \r{0}", e); + } + } + + public static void ReadDataFromFileASync(FileInfo file) + { + try + { + CrestronInvoke.BeginInvoke(o => _ReadDataFromFileASync(file)); + } + catch (Exception e) + { + Debug.LogMessage(LogEventLevel.Information, "Error: FileIO read failed: \r{0}", e); + } + } + + private static void _ReadDataFromFileASync(FileInfo file) + { + string data; + try + { + if (fileLock.TryEnter()) + { + DirectoryInfo dirInfo = new DirectoryInfo(file.Name); + Debug.LogMessage(LogEventLevel.Verbose, "FileIO Getting Data {0}", file.FullName); + + + if (File.Exists(file.FullName)) + { + using (StreamReader r = new StreamReader(file.FullName)) + { + data = r.ReadToEnd(); + } + } + else + { + Debug.LogMessage(LogEventLevel.Verbose, "File {0} Does not exsist", file.FullName); + data = ""; + } + GotFileEvent.Invoke(null, new FileEventArgs(data)); + } + else + { + Debug.LogMessage(LogEventLevel.Information, "FileIO Unable to enter FileLock"); + } + + } + catch (Exception e) + { + Debug.LogMessage(LogEventLevel.Information, "Error: FileIO read failed: \r{0}", e); + data = ""; + } + finally + { + if (fileLock != null && !fileLock.Disposed) + fileLock.Leave(); + } - } - public class FileEventArgs - { - public FileEventArgs(string data) { Data = data; } - public string Data { get; private set; } // readonly - } \ No newline at end of file + + } + + /// + /// + /// + /// + /// + public static void WriteDataToFile(string data, string filePath) + { + Thread _WriteFileThread; + _WriteFileThread = new Thread((O) => _WriteFileMethod(data, Global.FilePathPrefix + "/" + filePath), null, Thread.eThreadStartOptions.CreateSuspended); + _WriteFileThread.Priority = Thread.eThreadPriority.LowestPriority; + _WriteFileThread.Start(); + Debug.LogMessage(LogEventLevel.Information, "New WriteFile Thread"); + + } + + static object _WriteFileMethod(string data, string filePath) + { + Debug.LogMessage(LogEventLevel.Information, "Attempting to write file: '{0}'", filePath); + + try + { + if (fileLock.TryEnter()) + { + + using (StreamWriter sw = new StreamWriter(filePath)) + { + sw.Write(data); + sw.Flush(); + } + + } + else + { + Debug.LogMessage(LogEventLevel.Information, "FileIO Unable to enter FileLock"); + } + + } + catch (Exception e) + { + Debug.LogMessage(LogEventLevel.Error, "Error: FileIO write failed: \r{0}", e); + } + finally + { + if (fileLock != null && !fileLock.Disposed) + fileLock.Leave(); + + } + return null; + + } + + /// + /// + /// + /// + public static bool FileIoUnitTest() + { + var testData = "Testing FileIO"; + FileIO.WriteDataToFile(testData, "\\user\\FileIOTest.pdt"); + + var file = FileIO.GetFile("\\user\\*FileIOTest*"); + + var readData = FileIO.ReadDataFromFile(file); + Debug.LogMessage(LogEventLevel.Information, "Returned {0}", readData); + File.Delete(file.FullName); + if (testData == readData) + { + return true; + } + else + { + return false; + } + } + +} +public class FileEventArgs +{ + public FileEventArgs(string data) { Data = data; } + public string Data { get; private set; } // readonly + +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/src/PepperDash.Essentials.Core/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index 2f0df0b6..f57f3ffc 100644 --- a/src/PepperDash.Essentials.Core/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/src/PepperDash.Essentials.Core/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -1,7 +1,11 @@  +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.CrestronXml; using Crestron.SimplSharp.CrestronXml.Serialization; using Crestron.SimplSharpPro; @@ -11,10 +15,6 @@ using PepperDash.Core; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.DeviceTypeInterfaces; using Serilog.Events; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace PepperDash.Essentials.Core.Fusion; @@ -107,7 +107,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc JoinMap.SetCustomJoinData(customJoins); } } - + Room = room; _ipId = ipId; @@ -137,7 +137,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc File.Delete(oldGuidFilePath); } - _guidFileExists = File.Exists(guidFilePath); + _guidFileExists = File.Exists(guidFilePath); // Check if file exists if (!_guidFileExists) @@ -301,7 +301,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc if (File.Exists(filePath)) { - var json = File.ReadToEnd(filePath, Encoding.ASCII); + var json = File.ReadAllText(filePath, Encoding.ASCII); _guiDs = JsonConvert.DeserializeObject(json); @@ -466,7 +466,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc // Interface 1 if (InitialParametersClass.NumberOfEthernetInterfaces > 1) - // Only get these values if the processor has more than 1 NIC + // Only get these values if the processor has more than 1 NIC { _ip2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter( @@ -490,7 +490,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc { var join = JoinMap.ProgramNameStart.JoinNumber + i; var progNum = i + 1; - _program[i] = FusionRoom.CreateOffsetStringSig((uint) join, + _program[i] = FusionRoom.CreateOffsetStringSig((uint)join, string.Format("{0} {1}", JoinMap.ProgramNameStart.AttributeName, progNum), eSigIoMask.InputSigOnly); } } @@ -519,7 +519,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc { if (args.DeviceOnLine) { - CrestronInvoke.BeginInvoke( (o) => + CrestronInvoke.BeginInvoke((o) => { CrestronEnvironment.Sleep(200); @@ -658,7 +658,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc var extendTime = _currentMeeting.dtEnd - DateTime.Now; var extendMinutesRaw = extendTime.TotalMinutes; - extendMinutes += (int) Math.Round(extendMinutesRaw); + extendMinutes += (int)Math.Round(extendMinutesRaw); } @@ -766,11 +766,11 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc var parameters = actionResponse["Parameters"]; foreach (var isRegistered in from XmlElement parameter in parameters - where parameter.HasAttributes - select parameter.Attributes + where parameter.HasAttributes + select parameter.Attributes into attributes - where attributes["ID"].Value == "Registered" - select Int32.Parse(attributes["Value"].Value)) + where attributes["ID"].Value == "Registered" + select Int32.Parse(attributes["Value"].Value)) { switch (isRegistered) { @@ -827,9 +827,9 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc Debug.LogMessage(LogEventLevel.Debug, this, "DateTime from Fusion Server: {0}", currentTime); // Parse time and date from response and insert values - CrestronEnvironment.SetTimeAndDate((ushort) currentTime.Hour, (ushort) currentTime.Minute, - (ushort) currentTime.Second, (ushort) currentTime.Month, (ushort) currentTime.Day, - (ushort) currentTime.Year); + CrestronEnvironment.SetTimeAndDate((ushort)currentTime.Hour, (ushort)currentTime.Minute, + (ushort)currentTime.Second, (ushort)currentTime.Month, (ushort)currentTime.Day, + (ushort)currentTime.Year); Debug.LogMessage(LogEventLevel.Debug, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime()); } @@ -1093,7 +1093,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc foreach (var usageDevice in dict.Select(kvp => kvp.Value.SourceDevice).OfType()) { - usageDevice.UsageTracker = new UsageTracking(usageDevice as Device) {UsageIsTracked = true}; + usageDevice.UsageTracker = new UsageTracking(usageDevice as Device) { UsageIsTracked = true }; usageDevice.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; } } @@ -1279,7 +1279,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc foreach (var display in displays.Cast()) { - display.UsageTracker = new UsageTracking(display as Device) {UsageIsTracked = true}; + display.UsageTracker = new UsageTracking(display as Device) { UsageIsTracked = true }; display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; } @@ -1392,7 +1392,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc // Power on - var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint) joinOffset, displayName + "Power On", + var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { @@ -1403,7 +1403,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc }); // Power Off - var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 1, displayName + "Power Off", + var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { @@ -1421,7 +1421,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc } // Current Source - var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 8, + var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { @@ -1514,7 +1514,7 @@ public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupanc occRoom.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange; } RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString); - + RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig); //} diff --git a/src/PepperDash.Essentials.Core/Global/Global.cs b/src/PepperDash.Essentials.Core/Global/Global.cs index 5bde3e06..26bcbeb0 100644 --- a/src/PepperDash.Essentials.Core/Global/Global.cs +++ b/src/PepperDash.Essentials.Core/Global/Global.cs @@ -1,34 +1,32 @@ using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; using System.Linq; using System.Text.RegularExpressions; -using System.Globalization; using Crestron.SimplSharp; -using System.Collections.Generic; -using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.CrestronDataStore; using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.DM; - -using PepperDash.Core; -using PepperDash.Essentials.License; - using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; +using PepperDash.Core; +using PepperDash.Essentials.License; using Serilog.Events; namespace PepperDash.Essentials.Core; - public static class Global - { - public static CrestronControlSystem ControlSystem { get; set; } +public static class Global +{ + public static CrestronControlSystem ControlSystem { get; set; } public static eDevicePlatform Platform { get { return CrestronEnvironment.DevicePlatform; } } public static Dictionary EthernetAdapterInfoCollection { get; private set; } - public static LicenseManager LicenseManager { get; set; } + public static LicenseManager LicenseManager { get; set; } public static eCrestronSeries ProcessorSeries { get { return CrestronEnvironment.ProgramCompatibility; } } @@ -43,12 +41,12 @@ namespace PepperDash.Essentials.Core; { get { - if(ControlSystem.SystemControl != null) + if (ControlSystem.SystemControl != null) { - if(ControlSystem.SystemControl.SystemControlType > 0) + if (ControlSystem.SystemControl.SystemControlType > 0) { return true; - } + } } return false; } @@ -61,37 +59,37 @@ namespace PepperDash.Essentials.Core; { get { - if(ControlSystem.SystemControl != null) + if (ControlSystem.SystemControl != null) { - if(ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K150CSystemControl || + if (ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K150CSystemControl || ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K200CSystemControl || ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K250CSystemControl || ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K300CSystemControl || ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K350CSystemControl) { return true; - } + } } return false; } } - /// + /// /// True when the processor type is a DMPS 4K 200/300/250/350 variant /// public static bool ControlSystemIsDmps4k3xxType { get { - if(ControlSystem.SystemControl != null) + if (ControlSystem.SystemControl != null) { - if(ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K200CSystemControl || + if (ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K200CSystemControl || ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K250CSystemControl || ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K300CSystemControl || ControlSystem.SystemControl.SystemControlType == eSystemControlType.Dmps34K350CSystemControl) { return true; - } + } } return false; } @@ -105,11 +103,11 @@ namespace PepperDash.Essentials.Core; /// /// The file path prefix to the applciation directory /// - public static string ApplicationDirectoryPathPrefix + public static string ApplicationDirectoryPathPrefix { get { - return Crestron.SimplSharp.CrestronIO.Directory.GetApplicationDirectory(); + return Directory.GetCurrentDirectory(); } } @@ -166,37 +164,37 @@ namespace PepperDash.Essentials.Core; AssemblyVersion = assemblyVersion; } - public static bool IsRunningDevelopmentVersion(List developmentVersions, string minimumVersion) - { - if (Regex.Match(AssemblyVersion, @"^(\d*).(\d*).(\d*).*").Groups[1].Value == "0") - { + public static bool IsRunningDevelopmentVersion(List developmentVersions, string minimumVersion) + { + if (Regex.Match(AssemblyVersion, @"^(\d*).(\d*).(\d*).*").Groups[1].Value == "0") + { Debug.LogMessage(LogEventLevel.Verbose, "Running Local Build. Bypassing Dependency Check."); return true; - } + } - if (developmentVersions == null) - { - Debug.LogMessage(LogEventLevel.Information, - "Development Plugin does not specify a list of versions. Loading plugin may not work as expected. Checking Minumum version"); + if (developmentVersions == null) + { + Debug.LogMessage(LogEventLevel.Information, + "Development Plugin does not specify a list of versions. Loading plugin may not work as expected. Checking Minumum version"); return IsRunningMinimumVersionOrHigher(minimumVersion); - } + } Debug.LogMessage(LogEventLevel.Verbose, "Comparing running version '{0}' to minimum versions '{1}'", AssemblyVersion, developmentVersions); - var versionMatch = developmentVersions.FirstOrDefault(x => x == AssemblyVersion); + var versionMatch = developmentVersions.FirstOrDefault(x => x == AssemblyVersion); - if (String.IsNullOrEmpty(versionMatch)) - { - Debug.LogMessage(LogEventLevel.Information, "Essentials Build does not match any builds required for plugin load. Bypassing Plugin Load."); - return false; - } + if (String.IsNullOrEmpty(versionMatch)) + { + Debug.LogMessage(LogEventLevel.Information, "Essentials Build does not match any builds required for plugin load. Bypassing Plugin Load."); + return false; + } Debug.LogMessage(LogEventLevel.Verbose, "Essentials Build {0} matches list of development builds", AssemblyVersion); - return IsRunningMinimumVersionOrHigher(minimumVersion); + return IsRunningMinimumVersionOrHigher(minimumVersion); - } + } /// /// Checks to see if the running version meets or exceed the minimum specified version. For beta versions (0.xx.yy), will always return true. @@ -209,10 +207,10 @@ namespace PepperDash.Essentials.Core; if (String.IsNullOrEmpty(minimumVersion)) { - Debug.LogMessage(LogEventLevel.Information,"Plugin does not specify a minimum version. Loading plugin may not work as expected. Proceeding with loading plugin"); + Debug.LogMessage(LogEventLevel.Information, "Plugin does not specify a minimum version. Loading plugin may not work as expected. Proceeding with loading plugin"); return true; } - + var runtimeVersion = Regex.Match(AssemblyVersion, @"^(\d*).(\d*).(\d*).*"); var runtimeVersionMajor = Int16.Parse(runtimeVersion.Groups[1].Value); @@ -269,15 +267,15 @@ namespace PepperDash.Essentials.Core; */ } - static Global() - { - // Fire up CrestronDataStoreStatic - var err = CrestronDataStoreStatic.InitCrestronDataStore(); - if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) - { - CrestronConsole.PrintLine("Error starting CrestronDataStoreStatic: {0}", err); - return; - } + static Global() + { + // Fire up CrestronDataStoreStatic + var err = CrestronDataStoreStatic.InitCrestronDataStore(); + if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + { + CrestronConsole.PrintLine("Error starting CrestronDataStoreStatic: {0}", err); + return; + } try { @@ -290,6 +288,6 @@ namespace PepperDash.Essentials.Core; CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; } - } + } - } \ No newline at end of file +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/JoinMaps/JoinMapBase.cs b/src/PepperDash.Essentials.Core/JoinMaps/JoinMapBase.cs index 9cd6776e..581edc95 100644 --- a/src/PepperDash.Essentials.Core/JoinMaps/JoinMapBase.cs +++ b/src/PepperDash.Essentials.Core/JoinMaps/JoinMapBase.cs @@ -3,16 +3,14 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; -using System.Text; using System.Reflection; -using Crestron.SimplSharp.CrestronIO; +using System.Text; using Crestron.SimplSharp; - +using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core.Config; - -using Newtonsoft.Json; using Serilog.Events; namespace PepperDash.Essentials.Core; @@ -101,16 +99,16 @@ public abstract class JoinMapBaseAdvanced JoinOffset = joinStart - 1; } - protected JoinMapBaseAdvanced(uint joinStart, Type type):this(joinStart) + protected JoinMapBaseAdvanced(uint joinStart, Type type) : this(joinStart) { AddJoins(type); } protected void AddJoins(Type type) - { + { var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance) - .Where(f => f.IsDefined(typeof (JoinNameAttribute), true)).ToList(); + .Where(f => f.IsDefined(typeof(JoinNameAttribute), true)).ToList(); Debug.LogMessage(LogEventLevel.Debug, "Got {fields} with JoinNameAttribute", fields.Count); @@ -177,16 +175,16 @@ public abstract class JoinMapBaseAdvanced sb.Append($"## Digitals{lineEnding}"); sb.Append(lineEnding); // Get the joins of each type and print them - + var digitalSb = AppendJoinList(GetSortedJoins(digitals)); digitalSb.Append($"## Analogs{lineEnding}"); digitalSb.Append(lineEnding); - + var analogSb = AppendJoinList(GetSortedJoins(analogs)); analogSb.Append($"## Serials{lineEnding}"); analogSb.Append(lineEnding); - + var serialSb = AppendJoinList(GetSortedJoins(serials)); sb.EnsureCapacity(sb.Length + digitalSb.Length + analogSb.Length + serialSb.Length); @@ -244,7 +242,7 @@ public abstract class JoinMapBaseAdvanced 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(); + var descriptionLen = (from @join in joins select @join.Value into j select j.Metadata.Description.Length).Concat(new[] { 11 }).Max(); //build header sb.Append(string.Format(stringFormatter, @@ -488,7 +486,7 @@ public class JoinDataComplete /// public uint JoinNumber { - get { return _data.JoinNumber+ _joinOffset; } + get { return _data.JoinNumber + _joinOffset; } set { _data.JoinNumber = value; } } @@ -529,7 +527,7 @@ public class JoinNameAttribute : Attribute public JoinNameAttribute(string name) { - Debug.LogMessage(LogEventLevel.Verbose, "Setting Attribute Name: {0}",null, name); + Debug.LogMessage(LogEventLevel.Verbose, "Setting Attribute Name: {0}", null, name); _Name = name; } diff --git a/src/PepperDash.Essentials.Core/Presets/DevicePresets.cs b/src/PepperDash.Essentials.Core/Presets/DevicePresets.cs index 7346981b..b84f13d0 100644 --- a/src/PepperDash.Essentials.Core/Presets/DevicePresets.cs +++ b/src/PepperDash.Essentials.Core/Presets/DevicePresets.cs @@ -1,13 +1,12 @@ - + using System; using System.Collections.Generic; +using System.IO; using System.Text; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Newtonsoft.Json; using PepperDash.Core; - //using SSMono.IO; using PepperDash.Core.WebApi.Presets; using Serilog.Events; @@ -127,7 +126,7 @@ public class DevicePresetsModel : Device PresetsAreLoaded = false; try { - var pl = JsonConvert.DeserializeObject(File.ReadToEnd(_filePath, Encoding.ASCII)); + var pl = JsonConvert.DeserializeObject(File.ReadAllText(_filePath, Encoding.ASCII)); Name = pl.Name; PresetsList = pl.Channels; @@ -271,19 +270,20 @@ public class DevicePresetsModel : Device try { _fileOps.Enter(); - var pl = new PresetsList {Channels = PresetsList, Name = Name}; + var pl = new PresetsList { Channels = PresetsList, Name = Name }; var json = JsonConvert.SerializeObject(pl, Formatting.Indented); using (var file = File.Open(_filePath, FileMode.Truncate)) { - file.Write(json, Encoding.UTF8); + var bytes = Encoding.UTF8.GetBytes(json); + file.Write(bytes, 0, bytes.Length); } } finally { _fileOps.Leave(); } - + } private void OnPresetsSaved() diff --git a/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs b/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs index d5c582b8..cf4986cd 100644 --- a/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs +++ b/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharpPro; - using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; @@ -13,109 +12,109 @@ using Serilog.Events; namespace PepperDash.Essentials.Core.Config; +/// +/// Represents the configuration data for a single tie line between two routing ports. +/// +public class TieLineConfig +{ /// - /// Represents the configuration data for a single tie line between two routing ports. + /// The key of the source device. /// - public class TieLineConfig + public string SourceKey { get; set; } + + /// + /// The key of the source card (if applicable, e.g., in a modular chassis). + /// + public string SourceCard { get; set; } + + /// + /// The key of the source output port. + /// + public string SourcePort { get; set; } + + /// + /// The key of the destination device. + /// + public string DestinationKey { get; set; } + + /// + /// The key of the destination card (if applicable). + /// + public string DestinationCard { get; set; } + + /// + /// The key of the destination input port. + /// + public string DestinationPort { get; set; } + + /// + /// Optional override for the signal type of the tie line. If set, this overrides the destination port's type for routing calculations. + /// + [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public eRoutingSignalType? OverrideType { get; set; } + + /// + /// Returns the appropriate tie line for either a card-based device or + /// regular device with ports on-device. + /// + /// null if config data does not match ports, cards or devices + public TieLine GetTieLine() { - /// - /// The key of the source device. - /// - public string SourceKey { get; set; } - - /// - /// The key of the source card (if applicable, e.g., in a modular chassis). - /// - public string SourceCard { get; set; } - - /// - /// The key of the source output port. - /// - public string SourcePort { get; set; } - - /// - /// The key of the destination device. - /// - public string DestinationKey { get; set; } - - /// - /// The key of the destination card (if applicable). - /// - public string DestinationCard { get; set; } - - /// - /// The key of the destination input port. - /// - public string DestinationPort { get; set; } - - /// - /// Optional override for the signal type of the tie line. If set, this overrides the destination port's type for routing calculations. - /// - [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] - [JsonConverter(typeof(StringEnumConverter))] - public eRoutingSignalType? OverrideType { get; set; } - - /// - /// Returns the appropriate tie line for either a card-based device or - /// regular device with ports on-device. - /// - /// null if config data does not match ports, cards or devices - public TieLine GetTieLine() + Debug.LogMessage(LogEventLevel.Information, "Build TieLine: {0}", null, this); + // Get the source device + var sourceDev = DeviceManager.GetDeviceForKey(SourceKey) as IRoutingOutputs; + if (sourceDev == null) { - Debug.LogMessage(LogEventLevel.Information, "Build TieLine: {0}",null, this); - // Get the source device - var sourceDev = DeviceManager.GetDeviceForKey(SourceKey) as IRoutingOutputs; - if (sourceDev == null) - { - LogError("Routable source not found"); - return null; - } - - // Get the destination device - var destDev = DeviceManager.GetDeviceForKey(DestinationKey) as IRoutingInputs; - if (destDev == null) - { - LogError("Routable destination not found"); - return null; - } - - //Get the source port - var sourceOutputPort = sourceDev.OutputPorts[SourcePort]; - - if (sourceOutputPort == null) - { - LogError("Source does not contain port"); - return null; - } - - //Get the Destination port - var destinationInputPort = destDev.InputPorts[DestinationPort]; - - if (destinationInputPort == null) - { - LogError("Destination does not contain port"); - return null; - } - - return new TieLine(sourceOutputPort, destinationInputPort, OverrideType); + LogError("Routable source not found"); + return null; } - /// - /// Logs an error message related to creating this tie line configuration. - /// - /// The specific error message. - void LogError(string msg) + // Get the destination device + var destDev = DeviceManager.GetDeviceForKey(DestinationKey) as IRoutingInputs; + if (destDev == null) { - Debug.LogMessage(LogEventLevel.Error, "WARNING: Cannot create tie line: {message}:\r {tieLineConfig}",null, msg, this); + LogError("Routable destination not found"); + return null; } - /// - /// Returns a string representation of the tie line configuration. - /// - /// A string describing the source and destination of the configured tie line. - public override string ToString() + //Get the source port + var sourceOutputPort = sourceDev.OutputPorts[SourcePort]; + + if (sourceOutputPort == null) { - return string.Format("{0}.{1}.{2} --> {3}.{4}.{5}", SourceKey, SourceCard, SourcePort, - DestinationKey, DestinationCard, DestinationPort); + LogError("Source does not contain port"); + return null; } - } \ No newline at end of file + + //Get the Destination port + var destinationInputPort = destDev.InputPorts[DestinationPort]; + + if (destinationInputPort == null) + { + LogError("Destination does not contain port"); + return null; + } + + return new TieLine(sourceOutputPort, destinationInputPort, OverrideType); + } + + /// + /// Logs an error message related to creating this tie line configuration. + /// + /// The specific error message. + void LogError(string msg) + { + Debug.LogMessage(LogEventLevel.Error, "WARNING: Cannot create tie line: {message}:\r {tieLineConfig}", null, msg, this); + } + + /// + /// Returns a string representation of the tie line configuration. + /// + /// A string describing the source and destination of the configured tie line. + public override string ToString() + { + return string.Format("{0}.{1}.{2} --> {3}.{4}.{5}", SourceKey, SourceCard, SourcePort, + DestinationKey, DestinationCard, DestinationPort); + } +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/UI/TouchpanelBase.cs b/src/PepperDash.Essentials.Core/UI/TouchpanelBase.cs index ecc0fdae..00a56bed 100644 --- a/src/PepperDash.Essentials.Core/UI/TouchpanelBase.cs +++ b/src/PepperDash.Essentials.Core/UI/TouchpanelBase.cs @@ -1,19 +1,19 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using Crestron.SimplSharp; -using PepperDash.Essentials.Core; -using Crestron.SimplSharpPro.DeviceSupport; -using PepperDash.Core; -using Crestron.SimplSharpPro.UI; -using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.UI; +using PepperDash.Core; +using PepperDash.Essentials.Core; using Serilog.Events; namespace PepperDash.Essentials.Core.UI; -public abstract class TouchpanelBase: EssentialsDevice, IHasBasicTriListWithSmartObject +public abstract class TouchpanelBase : EssentialsDevice, IHasBasicTriListWithSmartObject { protected CrestronTouchpanelPropertiesConfig _config; public BasicTriListWithSmartObject Panel { get; private set; } @@ -29,7 +29,7 @@ public abstract class TouchpanelBase: EssentialsDevice, IHasBasicTriListWithSmar /// Touchpanel Configuration /// IP-ID to use for touch panel protected TouchpanelBase(string key, string name, BasicTriListWithSmartObject panel, CrestronTouchpanelPropertiesConfig config) - :base(key, name) + : base(key, name) { if (panel == null) @@ -52,9 +52,10 @@ public abstract class TouchpanelBase: EssentialsDevice, IHasBasicTriListWithSmar tsw.ButtonStateChange += Tsw_ButtonStateChange; } - _config = config; + _config = config; - AddPreActivationAction(() => { + AddPreActivationAction(() => + { if (Panel.Register() != eDeviceRegistrationUnRegistrationResponse.Success) Debug.LogMessage(LogEventLevel.Information, this, "WARNING: Registration failed. Continuing, but panel may not function: {0}", Panel.RegistrationFailureReason); @@ -154,21 +155,21 @@ public abstract class TouchpanelBase: EssentialsDevice, IHasBasicTriListWithSmar } private void Panel_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args) - { - Debug.LogMessage(LogEventLevel.Verbose, this, "Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); - var uo = args.Sig.UserObject; - if (uo is Action) - (uo as Action)(args.Sig.BoolValue); - else if (uo is Action) - (uo as Action)(args.Sig.UShortValue); - else if (uo is Action) - (uo as Action)(args.Sig.StringValue); - } - - private void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args) - { - var uo = args.Button.UserObject; - if(uo is Action) - (uo as Action)(args.Button.State == eButtonState.Pressed); - } + { + Debug.LogMessage(LogEventLevel.Verbose, this, "Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); + var uo = args.Sig.UserObject; + if (uo is Action) + (uo as Action)(args.Sig.BoolValue); + else if (uo is Action) + (uo as Action)(args.Sig.UShortValue); + else if (uo is Action) + (uo as Action)(args.Sig.StringValue); + } + + private void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args) + { + var uo = args.Button.UserObject; + if (uo is Action) + (uo as Action)(args.Button.State == eButtonState.Pressed); + } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/VideoCodec/VideoCodecBase.cs b/src/PepperDash.Essentials.Devices.Common/VideoCodec/VideoCodecBase.cs index d4974d93..56dfbd74 100644 --- a/src/PepperDash.Essentials.Devices.Common/VideoCodec/VideoCodecBase.cs +++ b/src/PepperDash.Essentials.Devices.Common/VideoCodec/VideoCodecBase.cs @@ -1,532 +1,515 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; -using Crestron.SimplSharp.CrestronIO; using System.Reflection; -using Crestron.SimplSharpPro.DeviceSupport; +using System.Text; using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; using PepperDash.Core; using PepperDash.Core.Intersystem; using PepperDash.Core.Intersystem.Tokens; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Bridges.JoinMaps; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Devices; using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Core.Routing; using PepperDash.Essentials.Devices.Common.Cameras; using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; -using PepperDash.Essentials.Core.Bridges.JoinMaps; -using Feedback = PepperDash.Essentials.Core.Feedback; using Serilog.Events; -using PepperDash.Essentials.Core.Routing; -using System.Text; +using Feedback = PepperDash.Essentials.Core.Feedback; namespace PepperDash.Essentials.Devices.Common.VideoCodec; - public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, - IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced, IHasStandbyMode +public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, + IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced, IHasStandbyMode +{ + private const int XSigEncoding = 28591; + protected const int MaxParticipants = 50; + private readonly byte[] _clearBytes = XSigHelpers.ClearOutputs(); + + private readonly IHasDirectory _directoryCodec; + private readonly BasicTriList _directoryTrilist; + private readonly VideoCodecControllerJoinMap _directoryJoinmap; + + protected string _timeFormatSpecifier; + protected string _dateFormatSpecifier; + + + protected VideoCodecBase(DeviceConfig config) + : base(config) { - private const int XSigEncoding = 28591; - protected const int MaxParticipants = 50; - private readonly byte[] _clearBytes = XSigHelpers.ClearOutputs(); - private readonly IHasDirectory _directoryCodec; - private readonly BasicTriList _directoryTrilist; - private readonly VideoCodecControllerJoinMap _directoryJoinmap; + StandbyIsOnFeedback = new BoolFeedback(StandbyIsOnFeedbackFunc); + PrivacyModeIsOnFeedback = new BoolFeedback(PrivacyModeIsOnFeedbackFunc); + VolumeLevelFeedback = new IntFeedback(VolumeLevelFeedbackFunc); + MuteFeedback = new BoolFeedback(MuteFeedbackFunc); + SharingSourceFeedback = new StringFeedback(SharingSourceFeedbackFunc); + SharingContentIsOnFeedback = new BoolFeedback(SharingContentIsOnFeedbackFunc); - protected string _timeFormatSpecifier; - protected string _dateFormatSpecifier; + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + MeetingsToDisplayFeedback = new IntFeedback(() => MeetingsToDisplay); + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); - protected VideoCodecBase(DeviceConfig config) - : base(config) + ActiveCalls = new List(); + } + + public IBasicCommunication Communication { get; protected set; } + + /// + /// An internal pseudo-source that is routable and connected to the osd input + /// + public DummyRoutingInputsDevice OsdSource { get; protected set; } + + public BoolFeedback StandbyIsOnFeedback { get; private set; } + + protected abstract Func PrivacyModeIsOnFeedbackFunc { get; } + protected abstract Func VolumeLevelFeedbackFunc { get; } + protected abstract Func MuteFeedbackFunc { get; } + protected abstract Func StandbyIsOnFeedbackFunc { get; } + + public List ActiveCalls { get; set; } + + public bool ShowSelfViewByDefault { get; protected set; } + + public bool SupportsCameraOff { get; protected set; } + public bool SupportsCameraAutoMode { get; protected set; } + + public bool IsReady { get; protected set; } + + public virtual List Feedbacks + { + get { + return new List + { + PrivacyModeIsOnFeedback, + SharingSourceFeedback + }; + } + } - StandbyIsOnFeedback = new BoolFeedback(StandbyIsOnFeedbackFunc); - PrivacyModeIsOnFeedback = new BoolFeedback(PrivacyModeIsOnFeedbackFunc); - VolumeLevelFeedback = new IntFeedback(VolumeLevelFeedbackFunc); - MuteFeedback = new BoolFeedback(MuteFeedbackFunc); - SharingSourceFeedback = new StringFeedback(SharingSourceFeedbackFunc); - SharingContentIsOnFeedback = new BoolFeedback(SharingContentIsOnFeedbackFunc); + protected abstract Func SharingSourceFeedbackFunc { get; } + protected abstract Func SharingContentIsOnFeedbackFunc { get; } - // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set - MeetingsToDisplayFeedback = new IntFeedback(() => MeetingsToDisplay); + #region ICodecAudio Members - InputPorts = new RoutingPortCollection(); - OutputPorts = new RoutingPortCollection(); + public abstract void PrivacyModeOn(); + public abstract void PrivacyModeOff(); + public abstract void PrivacyModeToggle(); + public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } - ActiveCalls = new List(); + + public BoolFeedback MuteFeedback { get; private set; } + + public abstract void MuteOff(); + + public abstract void MuteOn(); + + public abstract void SetVolume(ushort level); + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public abstract void MuteToggle(); + + public abstract void VolumeDown(bool pressRelease); + + + public abstract void VolumeUp(bool pressRelease); + + #endregion + + #region IHasContentSharing Members + + public abstract void StartSharing(); + public abstract void StopSharing(); + + public bool AutoShareContentWhileInCall { get; protected set; } + + public StringFeedback SharingSourceFeedback { get; private set; } + public BoolFeedback SharingContentIsOnFeedback { get; private set; } + + #endregion + + #region IHasDialer Members + + /// + /// Fires when the status of any active, dialing, or incoming call changes or is new + /// + public event EventHandler CallStatusChange; + + /// + /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected + /// + public bool IsInCall + { + get + { + var value = ActiveCalls != null && ActiveCalls.Any(c => c.IsActiveCall); + return value; + } + } + + public abstract void Dial(string number); + public abstract void EndCall(CodecActiveCallItem call); + public abstract void EndAllCalls(); + public abstract void AcceptCall(CodecActiveCallItem call); + public abstract void RejectCall(CodecActiveCallItem call); + public abstract void SendDtmf(string s); + public virtual void SendDtmf(string s, CodecActiveCallItem call) { } + + #endregion + + #region IRoutingInputsOutputs Members + + public RoutingPortCollection InputPorts { get; private set; } + + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + #region IUsageTracking Members + + /// + /// This object can be added by outside users of this class to provide usage tracking + /// for various services + /// + public UsageTracking UsageTracker { get; set; } + + #endregion + + #region iVideoCodecInfo Members + + public VideoCodecInfo CodecInfo { get; protected set; } + + #endregion + + public event EventHandler IsReadyChange; + public abstract void Dial(Meeting meeting); + + public virtual void Dial(IInvitableContact contact) + { + } + + public abstract void ExecuteSwitch(object selector); + + /// + /// Helper method to fire CallStatusChange event with old and new status + /// + protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call) + { + call.Status = newStatus; + + OnCallStatusChange(call); + } + + /// + /// + /// + /// + /// + /// + protected virtual void OnCallStatusChange(CodecActiveCallItem item) + { + CallStatusChange?.Invoke(this, new CodecCallStatusItemChangeEventArgs(item)); + + PrivacyModeIsOnFeedback.FireUpdate(); + + if (AutoShareContentWhileInCall) + { + StartSharing(); } - public IBasicCommunication Communication { get; protected set; } - - /// - /// An internal pseudo-source that is routable and connected to the osd input - /// - public DummyRoutingInputsDevice OsdSource { get; protected set; } - - public BoolFeedback StandbyIsOnFeedback { get; private set; } - - protected abstract Func PrivacyModeIsOnFeedbackFunc { get; } - protected abstract Func VolumeLevelFeedbackFunc { get; } - protected abstract Func MuteFeedbackFunc { get; } - protected abstract Func StandbyIsOnFeedbackFunc { get; } - - public List ActiveCalls { get; set; } - - public bool ShowSelfViewByDefault { get; protected set; } - - public bool SupportsCameraOff { get; protected set; } - public bool SupportsCameraAutoMode { get; protected set; } - - public bool IsReady { get; protected set; } - - public virtual List Feedbacks + if (UsageTracker != null) { - get + if (IsInCall && !UsageTracker.UsageTrackingStarted) { - return new List - { - PrivacyModeIsOnFeedback, - SharingSourceFeedback - }; + UsageTracker.StartDeviceUsage(); + } + else if (UsageTracker.UsageTrackingStarted && !IsInCall) + { + UsageTracker.EndDeviceUsage(); } } + } - protected abstract Func SharingSourceFeedbackFunc { get; } - protected abstract Func SharingContentIsOnFeedbackFunc { get; } - - #region ICodecAudio Members - - public abstract void PrivacyModeOn(); - public abstract void PrivacyModeOff(); - public abstract void PrivacyModeToggle(); - public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } - - - public BoolFeedback MuteFeedback { get; private set; } - - public abstract void MuteOff(); - - public abstract void MuteOn(); - - public abstract void SetVolume(ushort level); - - public IntFeedback VolumeLevelFeedback { get; private set; } - - public abstract void MuteToggle(); - - public abstract void VolumeDown(bool pressRelease); - - - public abstract void VolumeUp(bool pressRelease); - - #endregion - - #region IHasContentSharing Members - - public abstract void StartSharing(); - public abstract void StopSharing(); - - public bool AutoShareContentWhileInCall { get; protected set; } - - public StringFeedback SharingSourceFeedback { get; private set; } - public BoolFeedback SharingContentIsOnFeedback { get; private set; } - - #endregion - - #region IHasDialer Members - - /// - /// Fires when the status of any active, dialing, or incoming call changes or is new - /// - public event EventHandler CallStatusChange; - - /// - /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected - /// - public bool IsInCall - { - get + /// + /// Sets IsReady property and fires the event. Used for dependent classes to sync up their data. + /// + protected void SetIsReady() + { + CrestronInvoke.BeginInvoke((o) => { - var value = ActiveCalls != null && ActiveCalls.Any(c => c.IsActiveCall); - return value; - } - } - - public abstract void Dial(string number); - public abstract void EndCall(CodecActiveCallItem call); - public abstract void EndAllCalls(); - public abstract void AcceptCall(CodecActiveCallItem call); - public abstract void RejectCall(CodecActiveCallItem call); - public abstract void SendDtmf(string s); - public virtual void SendDtmf(string s, CodecActiveCallItem call) { } - - #endregion - - #region IRoutingInputsOutputs Members - - public RoutingPortCollection InputPorts { get; private set; } - - public RoutingPortCollection OutputPorts { get; private set; } - - #endregion - - #region IUsageTracking Members - - /// - /// This object can be added by outside users of this class to provide usage tracking - /// for various services - /// - public UsageTracking UsageTracker { get; set; } - - #endregion - - #region iVideoCodecInfo Members - - public VideoCodecInfo CodecInfo { get; protected set; } - - #endregion - - public event EventHandler IsReadyChange; - public abstract void Dial(Meeting meeting); - - public virtual void Dial(IInvitableContact contact) - { - } - - public abstract void ExecuteSwitch(object selector); - - /// - /// Helper method to fire CallStatusChange event with old and new status - /// - protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call) - { - call.Status = newStatus; - - OnCallStatusChange(call); - } - - /// - /// - /// - /// - /// - /// - protected virtual void OnCallStatusChange(CodecActiveCallItem item) - { - CallStatusChange?.Invoke(this, new CodecCallStatusItemChangeEventArgs(item)); - - PrivacyModeIsOnFeedback.FireUpdate(); - - if (AutoShareContentWhileInCall) - { - StartSharing(); - } - - if (UsageTracker != null) - { - if (IsInCall && !UsageTracker.UsageTrackingStarted) + try { - UsageTracker.StartDeviceUsage(); + IsReady = true; + IsReadyChange?.Invoke(this, new EventArgs()); } - else if (UsageTracker.UsageTrackingStarted && !IsInCall) + catch (Exception e) { - UsageTracker.EndDeviceUsage(); + Debug.LogMessage(LogEventLevel.Verbose, this, "Error in SetIsReady() : {0}", e); } - } + }); + } + + // **** DEBUGGING THINGS **** + /// + /// + /// + public virtual void ListCalls() + { + Debug.LogMessage(LogEventLevel.Debug, this, "Active Calls:"); + + var sb = new StringBuilder(); + foreach (var c in ActiveCalls) + { + sb.AppendFormat("id: {0} number: {1} -- name: {2} status: {3} onHold: {4}\r\n", c.Id, c.Number, c.Name, c.Status, c.IsOnHold); + } + Debug.LogMessage(LogEventLevel.Debug, this, "\n{0}\n", sb.ToString()); + } + + public abstract void StandbyActivate(); + + public abstract void StandbyDeactivate(); + + #region Implementation of IBridgeAdvanced + + public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge); + + /// + /// Use this method when using a plain VideoCodecControllerJoinMap + /// + /// + /// + /// + /// + /// + protected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, uint joinStart, string joinMapKey, + EiscApiAdvanced bridge) + { + var joinMap = new VideoCodecControllerJoinMap(joinStart); + + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + + if (customJoins != null) + { + joinMap.SetCustomJoinData(customJoins); } - /// - /// Sets IsReady property and fires the event. Used for dependent classes to sync up their data. - /// - protected void SetIsReady() + bridge?.AddJoinMap(Key, joinMap); + + LinkVideoCodecToApi(codec, trilist, joinMap); + + trilist.OnlineStatusChange += (device, args) => { - CrestronInvoke.BeginInvoke((o) => + if (!args.DeviceOnLine) return; + }; + } + + /// + /// Use this method when you need to pass in a join map that extends VideoCodecControllerJoinMap + /// + /// + /// + /// + protected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist {0}", trilist.ID.ToString("X")); + + LinkVideoCodecDtmfToApi(trilist, joinMap); + + LinkVideoCodecCallControlsToApi(trilist, joinMap); + + LinkVideoCodecContentSharingToApi(trilist, joinMap); + + LinkVideoCodecPrivacyToApi(trilist, joinMap); + + LinkVideoCodecVolumeToApi(trilist, joinMap); + + LinkVideoCodecInfoToApi(trilist, joinMap); + + // Register for this event to link any functions that require the codec to be ready first + codec.IsReadyChange += (o, a) => { - try + if (codec is IHasCodecCameras) { - IsReady = true; - IsReadyChange?.Invoke(this, new EventArgs()); - } - catch (Exception e) - { - Debug.LogMessage(LogEventLevel.Verbose, this, "Error in SetIsReady() : {0}", e); + LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap); } - }); + }; + + if (codec is ICommunicationMonitor) + { + LinkVideoCodecCommMonitorToApi(codec as ICommunicationMonitor, trilist, joinMap); } - // **** DEBUGGING THINGS **** - /// - /// - /// - public virtual void ListCalls() - { - Debug.LogMessage(LogEventLevel.Debug, this, "Active Calls:"); - var sb = new StringBuilder(); - foreach (var c in ActiveCalls) - { - sb.AppendFormat("id: {0} number: {1} -- name: {2} status: {3} onHold: {4}\r\n", c.Id, c.Number, c.Name, c.Status, c.IsOnHold); - } - Debug.LogMessage(LogEventLevel.Debug, this, "\n{0}\n", sb.ToString()); + if (codec is IHasCodecSelfView) + { + LinkVideoCodecSelfviewToApi(codec as IHasCodecSelfView, trilist, joinMap); } - public abstract void StandbyActivate(); - - public abstract void StandbyDeactivate(); - - #region Implementation of IBridgeAdvanced - - public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge); - - /// - /// Use this method when using a plain VideoCodecControllerJoinMap - /// - /// - /// - /// - /// - /// - protected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, uint joinStart, string joinMapKey, - EiscApiAdvanced bridge) + if (codec is IHasCameraAutoMode) { - var joinMap = new VideoCodecControllerJoinMap(joinStart); - - var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); - - if (customJoins != null) - { - joinMap.SetCustomJoinData(customJoins); - } - - bridge?.AddJoinMap(Key, joinMap); - - LinkVideoCodecToApi(codec, trilist, joinMap); - - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; - }; + trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode); + LinkVideoCodecCameraModeToApi(codec as IHasCameraAutoMode, trilist, joinMap); } - /// - /// Use this method when you need to pass in a join map that extends VideoCodecControllerJoinMap - /// - /// - /// - /// - protected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + if (codec is IHasCameraOff) { - Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist {0}", trilist.ID.ToString("X")); + trilist.SetBool(joinMap.CameraSupportsOffMode.JoinNumber, SupportsCameraOff); + LinkVideoCodecCameraOffToApi(codec as IHasCameraOff, trilist, joinMap); + } - LinkVideoCodecDtmfToApi(trilist, joinMap); + if (codec is IHasCodecLayouts) + { + LinkVideoCodecCameraLayoutsToApi(codec as IHasCodecLayouts, trilist, joinMap); + } - LinkVideoCodecCallControlsToApi(trilist, joinMap); - LinkVideoCodecContentSharingToApi(trilist, joinMap); + if (codec is IHasSelfviewPosition) + { + LinkVideoCodecSelfviewPositionToApi(codec as IHasSelfviewPosition, trilist, joinMap); + } - LinkVideoCodecPrivacyToApi(trilist, joinMap); + if (codec is IHasDirectory) + { + LinkVideoCodecDirectoryToApi(codec as IHasDirectory, trilist, joinMap); + } - LinkVideoCodecVolumeToApi(trilist, joinMap); + if (codec is IHasScheduleAwareness) + { + LinkVideoCodecScheduleToApi(codec as IHasScheduleAwareness, trilist, joinMap); + } - LinkVideoCodecInfoToApi(trilist, joinMap); + if (codec is IHasParticipants) + { + LinkVideoCodecParticipantsToApi(codec as IHasParticipants, trilist, joinMap); + } - // Register for this event to link any functions that require the codec to be ready first - codec.IsReadyChange += (o, a) => - { - if (codec is IHasCodecCameras) - { - LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap); - } - }; + if (codec is IHasFarEndContentStatus) + { + (codec as IHasFarEndContentStatus).ReceivingContent.LinkInputSig(trilist.BooleanInput[joinMap.RecievingContent.JoinNumber]); + } - if (codec is ICommunicationMonitor) + if (codec is IHasPhoneDialing) + { + LinkVideoCodecPhoneToApi(codec as IHasPhoneDialing, trilist, joinMap); + } + + if (codec is IHasCallHistory) + { + LinkVideoCodecCallHistoryToApi(codec as IHasCallHistory, trilist, joinMap); + } + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + if (codec is IHasDirectory) { - LinkVideoCodecCommMonitorToApi(codec as ICommunicationMonitor, trilist, joinMap); + (codec as IHasDirectory).SetCurrentDirectoryToRoot(); } - - if (codec is IHasCodecSelfView) + if (codec is IHasScheduleAwareness) { - LinkVideoCodecSelfviewToApi(codec as IHasCodecSelfView, trilist, joinMap); + (codec as IHasScheduleAwareness).GetSchedule(); + } + + if (codec is IHasParticipants) + { + UpdateParticipantsXSig((codec as IHasParticipants).Participants.CurrentParticipants); } if (codec is IHasCameraAutoMode) { trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode); - LinkVideoCodecCameraModeToApi(codec as IHasCameraAutoMode, trilist, joinMap); + + (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCodecSelfView) + { + (codec as IHasCodecSelfView).SelfviewIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCameraAutoMode) + { + (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); } if (codec is IHasCameraOff) { - trilist.SetBool(joinMap.CameraSupportsOffMode.JoinNumber, SupportsCameraOff); - LinkVideoCodecCameraOffToApi(codec as IHasCameraOff, trilist, joinMap); - } - - if (codec is IHasCodecLayouts) - { - LinkVideoCodecCameraLayoutsToApi(codec as IHasCodecLayouts, trilist, joinMap); - } - - - if (codec is IHasSelfviewPosition) - { - LinkVideoCodecSelfviewPositionToApi(codec as IHasSelfviewPosition, trilist, joinMap); - } - - if (codec is IHasDirectory) - { - LinkVideoCodecDirectoryToApi(codec as IHasDirectory, trilist, joinMap); - } - - if (codec is IHasScheduleAwareness) - { - LinkVideoCodecScheduleToApi(codec as IHasScheduleAwareness, trilist, joinMap); - } - - if (codec is IHasParticipants) - { - LinkVideoCodecParticipantsToApi(codec as IHasParticipants, trilist, joinMap); - } - - if (codec is IHasFarEndContentStatus) - { - (codec as IHasFarEndContentStatus).ReceivingContent.LinkInputSig(trilist.BooleanInput[joinMap.RecievingContent.JoinNumber]); + (codec as IHasCameraOff).CameraIsOffFeedback.FireUpdate(); } if (codec is IHasPhoneDialing) { - LinkVideoCodecPhoneToApi(codec as IHasPhoneDialing, trilist, joinMap); + (codec as IHasPhoneDialing).PhoneOffHookFeedback.FireUpdate(); } - if (codec is IHasCallHistory) - { - LinkVideoCodecCallHistoryToApi(codec as IHasCallHistory, trilist, joinMap); - } - - trilist.OnlineStatusChange += (device, args) => + if (codec is IHasCallHistory) { - if (!args.DeviceOnLine) return; + UpdateCallHistory((codec as IHasCallHistory), trilist, joinMap); + } - if (codec is IHasDirectory) - { - (codec as IHasDirectory).SetCurrentDirectoryToRoot(); - } + SharingContentIsOnFeedback.FireUpdate(); + }; + } - if (codec is IHasScheduleAwareness) - { - (codec as IHasScheduleAwareness).GetSchedule(); - } + private void LinkVideoCodecInfoToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetBool(joinMap.MultiSiteOptionIsEnabled.JoinNumber, this.CodecInfo.MultiSiteOptionIsEnabled); + trilist.SetBool(joinMap.AutoAnswerEnabled.JoinNumber, this.CodecInfo.AutoAnswerEnabled); + trilist.SetString(joinMap.DeviceIpAddresss.JoinNumber, this.CodecInfo.IpAddress); + trilist.SetString(joinMap.SipPhoneNumber.JoinNumber, this.CodecInfo.SipPhoneNumber); + trilist.SetString(joinMap.E164Alias.JoinNumber, this.CodecInfo.E164Alias); + trilist.SetString(joinMap.H323Id.JoinNumber, this.CodecInfo.H323Id); + trilist.SetString(joinMap.SipUri.JoinNumber, this.CodecInfo.SipUri); - if (codec is IHasParticipants) - { - UpdateParticipantsXSig((codec as IHasParticipants).Participants.CurrentParticipants); - } - - if (codec is IHasCameraAutoMode) - { - trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode); - - (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); - } - - if (codec is IHasCodecSelfView) - { - (codec as IHasCodecSelfView).SelfviewIsOnFeedback.FireUpdate(); - } - - if (codec is IHasCameraAutoMode) - { - (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); - } - - if (codec is IHasCameraOff) - { - (codec as IHasCameraOff).CameraIsOffFeedback.FireUpdate(); - } - - if (codec is IHasPhoneDialing) - { - (codec as IHasPhoneDialing).PhoneOffHookFeedback.FireUpdate(); - } - - if (codec is IHasCallHistory) - { - UpdateCallHistory((codec as IHasCallHistory), trilist, joinMap); - } - - SharingContentIsOnFeedback.FireUpdate(); - }; - } - - private void LinkVideoCodecInfoToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetBool(joinMap.MultiSiteOptionIsEnabled.JoinNumber, this.CodecInfo.MultiSiteOptionIsEnabled); - trilist.SetBool(joinMap.AutoAnswerEnabled.JoinNumber, this.CodecInfo.AutoAnswerEnabled); - trilist.SetString(joinMap.DeviceIpAddresss.JoinNumber, this.CodecInfo.IpAddress); - trilist.SetString(joinMap.SipPhoneNumber.JoinNumber, this.CodecInfo.SipPhoneNumber); - trilist.SetString(joinMap.E164Alias.JoinNumber, this.CodecInfo.E164Alias); - trilist.SetString(joinMap.H323Id.JoinNumber, this.CodecInfo.H323Id); - trilist.SetString(joinMap.SipUri.JoinNumber, this.CodecInfo.SipUri); - - trilist.OnlineStatusChange += (o, a) => - { - if (a.DeviceOnLine) - { - trilist.SetBool(joinMap.MultiSiteOptionIsEnabled.JoinNumber, this.CodecInfo.MultiSiteOptionIsEnabled); - trilist.SetBool(joinMap.AutoAnswerEnabled.JoinNumber, this.CodecInfo.AutoAnswerEnabled); - trilist.SetString(joinMap.DeviceIpAddresss.JoinNumber, this.CodecInfo.IpAddress); - trilist.SetString(joinMap.SipPhoneNumber.JoinNumber, this.CodecInfo.SipPhoneNumber); - trilist.SetString(joinMap.E164Alias.JoinNumber, this.CodecInfo.E164Alias); - trilist.SetString(joinMap.H323Id.JoinNumber, this.CodecInfo.H323Id); - trilist.SetString(joinMap.SipUri.JoinNumber, this.CodecInfo.SipUri); - } - }; - } - - private void LinkVideoCodecPhoneToApi(IHasPhoneDialing codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + trilist.OnlineStatusChange += (o, a) => { - codec.PhoneOffHookFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PhoneHookState.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.DialPhone.JoinNumber, - () => codec.DialPhoneCall(trilist.StringOutput[joinMap.PhoneDialString.JoinNumber].StringValue)); - - trilist.SetSigFalseAction(joinMap.HangUpPhone.JoinNumber, codec.EndPhoneCall); - } - - private void LinkVideoCodecSelfviewPositionToApi(IHasSelfviewPosition codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.SelfviewPosition.JoinNumber, codec.SelfviewPipPositionToggle); - - codec.SelfviewPipPositionFeedback.LinkInputSig(trilist.StringInput[joinMap.SelfviewPositionFb.JoinNumber]); - } - - private void LinkVideoCodecCameraOffToApi(IHasCameraOff codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraModeOff.JoinNumber, codec.CameraOff); - - codec.CameraIsOffFeedback.OutputChange += (o, a) => + if (a.DeviceOnLine) { - if (a.BoolValue) - { - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - return; - } + trilist.SetBool(joinMap.MultiSiteOptionIsEnabled.JoinNumber, this.CodecInfo.MultiSiteOptionIsEnabled); + trilist.SetBool(joinMap.AutoAnswerEnabled.JoinNumber, this.CodecInfo.AutoAnswerEnabled); + trilist.SetString(joinMap.DeviceIpAddresss.JoinNumber, this.CodecInfo.IpAddress); + trilist.SetString(joinMap.SipPhoneNumber.JoinNumber, this.CodecInfo.SipPhoneNumber); + trilist.SetString(joinMap.E164Alias.JoinNumber, this.CodecInfo.E164Alias); + trilist.SetString(joinMap.H323Id.JoinNumber, this.CodecInfo.H323Id); + trilist.SetString(joinMap.SipUri.JoinNumber, this.CodecInfo.SipUri); + } + }; + } - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + private void LinkVideoCodecPhoneToApi(IHasPhoneDialing codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.PhoneOffHookFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PhoneHookState.JoinNumber]); + trilist.SetSigFalseAction(joinMap.DialPhone.JoinNumber, + () => codec.DialPhoneCall(trilist.StringOutput[joinMap.PhoneDialString.JoinNumber].StringValue)); - if (!(codec is IHasCameraAutoMode autoCodec)) return; + trilist.SetSigFalseAction(joinMap.HangUpPhone.JoinNumber, codec.EndPhoneCall); + } - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoCodec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoCodec.CameraAutoModeIsOnFeedback.BoolValue); - }; + private void LinkVideoCodecSelfviewPositionToApi(IHasSelfviewPosition codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.SelfviewPosition.JoinNumber, codec.SelfviewPipPositionToggle); - if (codec.CameraIsOffFeedback.BoolValue) + codec.SelfviewPipPositionFeedback.LinkInputSig(trilist.StringInput[joinMap.SelfviewPositionFb.JoinNumber]); + } + + private void LinkVideoCodecCameraOffToApi(IHasCameraOff codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraModeOff.JoinNumber, codec.CameraOff); + + codec.CameraIsOffFeedback.OutputChange += (o, a) => + { + if (a.BoolValue) { trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); @@ -537,1452 +520,1470 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec; trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - if (!(codec is IHasCameraAutoMode autoModeCodec)) return; + if (!(codec is IHasCameraAutoMode autoCodec)) return; - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoCodec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoCodec.CameraAutoModeIsOnFeedback.BoolValue); + }; + + if (codec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + return; } - private void LinkVideoCodecVolumeToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMuteOn.JoinNumber]); - MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.VolumeMuteOn.JoinNumber, MuteOn); - trilist.SetSigFalseAction(joinMap.VolumeMuteOff.JoinNumber, MuteOff); - trilist.SetSigFalseAction(joinMap.VolumeMuteToggle.JoinNumber, MuteToggle); - - VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.VolumeLevel.JoinNumber]); - - trilist.SetBoolSigAction(joinMap.VolumeUp.JoinNumber, VolumeUp); - trilist.SetBoolSigAction(joinMap.VolumeDown.JoinNumber, VolumeDown); - - trilist.SetUShortSigAction(joinMap.VolumeLevel.JoinNumber, SetVolume); - - } - - private void LinkVideoCodecPrivacyToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - PrivacyModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.MicMuteOn.JoinNumber]); - PrivacyModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.MicMuteOff.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.MicMuteOn.JoinNumber, PrivacyModeOn); - trilist.SetSigFalseAction(joinMap.MicMuteOff.JoinNumber, PrivacyModeOff); - trilist.SetSigFalseAction(joinMap.MicMuteToggle.JoinNumber, PrivacyModeToggle); - } - - private void LinkVideoCodecCommMonitorToApi(ICommunicationMonitor codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - } - - private void LinkVideoCodecParticipantsToApi(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - // make sure to update the values when the EISC comes online - trilist.OnlineStatusChange += (sender, args) => - { - if (sender.IsOnline) - { - UpdateParticipantsXSig(codec, trilist, joinMap); - } - }; - - // set actions and update the values when the list changes - codec.Participants.ParticipantsListHasChanged += (sender, args) => - { - SetParticipantActions(trilist, joinMap, codec.Participants.CurrentParticipants); - - UpdateParticipantsXSig(codec, trilist, joinMap); - }; - - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; - - // TODO [ ] Issue #868 - trilist.SetString(joinMap.CurrentParticipants.JoinNumber, "\xFC"); - UpdateParticipantsXSig(codec, trilist, joinMap); - }; - } - - private void UpdateParticipantsXSig(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - string participantsXSig; - - if (codec.Participants.CurrentParticipants.Count == 0) - { - participantsXSig = Encoding.GetEncoding(XSigEncoding).GetString(_clearBytes, 0, _clearBytes.Length); - trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); - trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); - return; - } - - participantsXSig = UpdateParticipantsXSig(codec.Participants.CurrentParticipants); - - trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); - - trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); - } - - /// - /// Sets the actions for each participant in the list - /// - private void SetParticipantActions(BasicTriList trilist, VideoCodecControllerJoinMap joinMap, List currentParticipants) - { - uint index = 0; // track the index of the participant in the - - foreach (var participant in currentParticipants) - { - var p = participant; - if (index > MaxParticipants) break; - - if (this is IHasParticipantAudioMute audioMuteCodec) - { - trilist.SetSigFalseAction(joinMap.ParticipantAudioMuteToggleStart.JoinNumber + index, - () => audioMuteCodec.ToggleAudioForParticipant(p.UserId)); - - trilist.SetSigFalseAction(joinMap.ParticipantVideoMuteToggleStart.JoinNumber + index, - () => audioMuteCodec.ToggleVideoForParticipant(p.UserId)); - } - - if (this is IHasParticipantPinUnpin pinCodec) - { - trilist.SetSigFalseAction(joinMap.ParticipantPinToggleStart.JoinNumber + index, - () => pinCodec.ToggleParticipantPinState(p.UserId, pinCodec.ScreenIndexToPinUserTo)); - } - - index++; - } - - // Clear out any previously set actions - while (index < MaxParticipants) - { - trilist.ClearBoolSigAction(joinMap.ParticipantAudioMuteToggleStart.JoinNumber + index); - trilist.ClearBoolSigAction(joinMap.ParticipantVideoMuteToggleStart.JoinNumber + index); - trilist.ClearBoolSigAction(joinMap.ParticipantPinToggleStart.JoinNumber + index); - - index++; - } - } - - private string UpdateParticipantsXSig(List currentParticipants) - { - const int maxParticipants = MaxParticipants; - const int maxDigitals = 7; - const int maxStrings = 1; - const int maxAnalogs = 1; - const int offset = maxDigitals + maxStrings + maxAnalogs; // 9 - var digitalIndex = (maxStrings + maxAnalogs) * maxParticipants; // 100 - var stringIndex = 0; - var analogIndex = stringIndex + maxParticipants; - var meetingIndex = 0; - - var tokenArray = new XSigToken[maxParticipants * offset]; - - foreach (var participant in currentParticipants) - { - if (meetingIndex >= maxParticipants * offset) break; - - // Debug.LogMessage(LogEventLevel.Verbose, this, - //@"Updating Participant on xsig: - //Name: {0} (s{9}) - //AudioMute: {1} (d{10}) - //VideoMute: {2} (d{11}) - //CanMuteVideo: {3} (d{12}) - //CanUMuteVideo: {4} (d{13}) - //IsHost: {5} (d{14}) - //HandIsRaised: {6} (d{15}) - //IsPinned: {7} (d{16}) - //ScreenIndexIsPinnedTo: {8} (a{17}) - //", - // participant.Name, - // participant.AudioMuteFb, - // participant.VideoMuteFb, - // participant.CanMuteVideo, - // participant.CanUnmuteVideo, - // participant.IsHost, - // participant.HandIsRaisedFb, - // participant.IsPinnedFb, - // participant.ScreenIndexIsPinnedToFb, - // stringIndex + 1, - // digitalIndex + 1, - // digitalIndex + 2, - // digitalIndex + 3, - // digitalIndex + 4, - // digitalIndex + 5, - // digitalIndex + 6, - // digitalIndex + 7, - // analogIndex + 1 - // ); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, participant.AudioMuteFb); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, participant.VideoMuteFb); - tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, participant.CanMuteVideo); - tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, participant.CanUnmuteVideo); - tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, participant.IsHost); - tokenArray[digitalIndex + 5] = new XSigDigitalToken(digitalIndex + 6, participant.HandIsRaisedFb); - tokenArray[digitalIndex + 6] = new XSigDigitalToken(digitalIndex + 7, participant.IsPinnedFb); + if (!(codec is IHasCameraAutoMode autoModeCodec)) return; - Debug.LogMessage(LogEventLevel.Verbose, this, "Index: {0} byte value: {1}", digitalIndex + 7, ComTextHelper.GetEscapedText(tokenArray[digitalIndex + 6].GetBytes())); + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); + } - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, participant.Name); + private void LinkVideoCodecVolumeToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMuteOn.JoinNumber]); + MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]); - //analogs - tokenArray[analogIndex] = new XSigAnalogToken(analogIndex + 1, (ushort)participant.ScreenIndexIsPinnedToFb); + trilist.SetSigFalseAction(joinMap.VolumeMuteOn.JoinNumber, MuteOn); + trilist.SetSigFalseAction(joinMap.VolumeMuteOff.JoinNumber, MuteOff); + trilist.SetSigFalseAction(joinMap.VolumeMuteToggle.JoinNumber, MuteToggle); - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - analogIndex += maxAnalogs; - } + VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.VolumeLevel.JoinNumber]); - while (meetingIndex < maxParticipants * offset) - { - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); - tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, false); - tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, false); - tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, false); - tokenArray[digitalIndex + 5] = new XSigDigitalToken(digitalIndex + 6, false); - tokenArray[digitalIndex + 6] = new XSigDigitalToken(digitalIndex + 7, false); + trilist.SetBoolSigAction(joinMap.VolumeUp.JoinNumber, VolumeUp); + trilist.SetBoolSigAction(joinMap.VolumeDown.JoinNumber, VolumeDown); - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); + trilist.SetUShortSigAction(joinMap.VolumeLevel.JoinNumber, SetVolume); - //analogs - tokenArray[analogIndex] = new XSigAnalogToken(analogIndex + 1, 0); + } - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - analogIndex += maxAnalogs; - } + private void LinkVideoCodecPrivacyToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + PrivacyModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.MicMuteOn.JoinNumber]); + PrivacyModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.MicMuteOff.JoinNumber]); - var returnString = GetXSigString(tokenArray); + trilist.SetSigFalseAction(joinMap.MicMuteOn.JoinNumber, PrivacyModeOn); + trilist.SetSigFalseAction(joinMap.MicMuteOff.JoinNumber, PrivacyModeOff); + trilist.SetSigFalseAction(joinMap.MicMuteToggle.JoinNumber, PrivacyModeToggle); + } - //Debug.LogMessage(LogEventLevel.Verbose, this, "{0}", ComTextHelper.GetEscapedText(Encoding.GetEncoding(28591).GetBytes(returnString))); + private void LinkVideoCodecCommMonitorToApi(ICommunicationMonitor codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + } - - return returnString; - } - - private void LinkVideoCodecContentSharingToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - SharingContentIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SourceShareStart.JoinNumber]); - SharingContentIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.SourceShareEnd.JoinNumber]); - - SharingSourceFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentSource.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.SourceShareStart.JoinNumber, StartSharing); - trilist.SetSigFalseAction(joinMap.SourceShareEnd.JoinNumber, StopSharing); - - trilist.SetBoolSigAction(joinMap.SourceShareAutoStart.JoinNumber, b => AutoShareContentWhileInCall = b); - } - - private List _currentMeetings = new List(); - - private void LinkVideoCodecScheduleToApi(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.UpdateMeetings.JoinNumber, codec.GetSchedule); - - trilist.SetUShortSigAction(joinMap.MinutesBeforeMeetingStart.JoinNumber, (i) => - { - codec.CodecSchedule.MeetingWarningMinutes = i; - }); - - - for (uint i = 0; i < joinMap.DialMeetingStart.JoinSpan; i++) - { - Debug.LogMessage(LogEventLevel.Debug, this, "Setting action to Dial Meeting {0} to digital join {1}", i + 1, joinMap.DialMeetingStart.JoinNumber + i); - var joinNumber = joinMap.DialMeetingStart.JoinNumber + i; - var mtg = i + 1; - var index = (int)i; - - trilist.SetSigFalseAction(joinNumber, () => - { - Debug.LogMessage(LogEventLevel.Debug, this, "Meeting {0} Selected (EISC dig-o{1}) > _currentMeetings[{2}].Id: {3}, Title: {4}", - mtg, joinMap.DialMeetingStart.JoinNumber + i, index, _currentMeetings[index].Id, _currentMeetings[index].Title); - if (_currentMeetings[index] != null) - Dial(_currentMeetings[index]); - }); - } - - codec.CodecSchedule.MeetingsListHasChanged += (sender, args) => UpdateMeetingsList(codec, trilist, joinMap); - codec.CodecSchedule.MeetingEventChange += (sender, args) => + private void LinkVideoCodecParticipantsToApi(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + // make sure to update the values when the EISC comes online + trilist.OnlineStatusChange += (sender, args) => { - if (args.ChangeType == eMeetingEventChangeType.MeetingStartWarning) + if (sender.IsOnline) { - UpdateMeetingsList(codec, trilist, joinMap); + UpdateParticipantsXSig(codec, trilist, joinMap); } }; - trilist.SetUShortSigAction(joinMap.MeetingsToDisplay.JoinNumber, m => MeetingsToDisplay = m); - MeetingsToDisplayFeedback.LinkInputSig(trilist.UShortInput[joinMap.MeetingsToDisplay.JoinNumber]); + // set actions and update the values when the list changes + codec.Participants.ParticipantsListHasChanged += (sender, args) => + { + SetParticipantActions(trilist, joinMap, codec.Participants.CurrentParticipants); - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; + UpdateParticipantsXSig(codec, trilist, joinMap); + }; - // TODO [ ] Issue #868 - trilist.SetString(joinMap.Schedule.JoinNumber, "\xFC"); - UpdateMeetingsList(codec, trilist, joinMap); - // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set - MeetingsToDisplayFeedback.LinkInputSig(trilist.UShortInput[joinMap.MeetingsToDisplay.JoinNumber]); - }; - } + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; - private void UpdateMeetingsList(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + // TODO [ ] Issue #868 + trilist.SetString(joinMap.CurrentParticipants.JoinNumber, "\xFC"); + UpdateParticipantsXSig(codec, trilist, joinMap); + }; + } + + private void UpdateParticipantsXSig(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + string participantsXSig; + + if (codec.Participants.CurrentParticipants.Count == 0) + { + participantsXSig = Encoding.GetEncoding(XSigEncoding).GetString(_clearBytes, 0, _clearBytes.Length); + trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); + trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); + return; + } + + participantsXSig = UpdateParticipantsXSig(codec.Participants.CurrentParticipants); + + trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); + + trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); + } + + /// + /// Sets the actions for each participant in the list + /// + private void SetParticipantActions(BasicTriList trilist, VideoCodecControllerJoinMap joinMap, List currentParticipants) + { + uint index = 0; // track the index of the participant in the + + foreach (var participant in currentParticipants) + { + var p = participant; + if (index > MaxParticipants) break; + + if (this is IHasParticipantAudioMute audioMuteCodec) + { + trilist.SetSigFalseAction(joinMap.ParticipantAudioMuteToggleStart.JoinNumber + index, + () => audioMuteCodec.ToggleAudioForParticipant(p.UserId)); + + trilist.SetSigFalseAction(joinMap.ParticipantVideoMuteToggleStart.JoinNumber + index, + () => audioMuteCodec.ToggleVideoForParticipant(p.UserId)); + } + + if (this is IHasParticipantPinUnpin pinCodec) + { + trilist.SetSigFalseAction(joinMap.ParticipantPinToggleStart.JoinNumber + index, + () => pinCodec.ToggleParticipantPinState(p.UserId, pinCodec.ScreenIndexToPinUserTo)); + } + + index++; + } + + // Clear out any previously set actions + while (index < MaxParticipants) + { + trilist.ClearBoolSigAction(joinMap.ParticipantAudioMuteToggleStart.JoinNumber + index); + trilist.ClearBoolSigAction(joinMap.ParticipantVideoMuteToggleStart.JoinNumber + index); + trilist.ClearBoolSigAction(joinMap.ParticipantPinToggleStart.JoinNumber + index); + + index++; + } + } + + private string UpdateParticipantsXSig(List currentParticipants) + { + const int maxParticipants = MaxParticipants; + const int maxDigitals = 7; + const int maxStrings = 1; + const int maxAnalogs = 1; + const int offset = maxDigitals + maxStrings + maxAnalogs; // 9 + var digitalIndex = (maxStrings + maxAnalogs) * maxParticipants; // 100 + var stringIndex = 0; + var analogIndex = stringIndex + maxParticipants; + var meetingIndex = 0; + + var tokenArray = new XSigToken[maxParticipants * offset]; + + foreach (var participant in currentParticipants) + { + if (meetingIndex >= maxParticipants * offset) break; + + // Debug.LogMessage(LogEventLevel.Verbose, this, + //@"Updating Participant on xsig: + //Name: {0} (s{9}) + //AudioMute: {1} (d{10}) + //VideoMute: {2} (d{11}) + //CanMuteVideo: {3} (d{12}) + //CanUMuteVideo: {4} (d{13}) + //IsHost: {5} (d{14}) + //HandIsRaised: {6} (d{15}) + //IsPinned: {7} (d{16}) + //ScreenIndexIsPinnedTo: {8} (a{17}) + //", + // participant.Name, + // participant.AudioMuteFb, + // participant.VideoMuteFb, + // participant.CanMuteVideo, + // participant.CanUnmuteVideo, + // participant.IsHost, + // participant.HandIsRaisedFb, + // participant.IsPinnedFb, + // participant.ScreenIndexIsPinnedToFb, + // stringIndex + 1, + // digitalIndex + 1, + // digitalIndex + 2, + // digitalIndex + 3, + // digitalIndex + 4, + // digitalIndex + 5, + // digitalIndex + 6, + // digitalIndex + 7, + // analogIndex + 1 + // ); + + + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, participant.AudioMuteFb); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, participant.VideoMuteFb); + tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, participant.CanMuteVideo); + tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, participant.CanUnmuteVideo); + tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, participant.IsHost); + tokenArray[digitalIndex + 5] = new XSigDigitalToken(digitalIndex + 6, participant.HandIsRaisedFb); + tokenArray[digitalIndex + 6] = new XSigDigitalToken(digitalIndex + 7, participant.IsPinnedFb); + + Debug.LogMessage(LogEventLevel.Verbose, this, "Index: {0} byte value: {1}", digitalIndex + 7, ComTextHelper.GetEscapedText(tokenArray[digitalIndex + 6].GetBytes())); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, participant.Name); + + //analogs + tokenArray[analogIndex] = new XSigAnalogToken(analogIndex + 1, (ushort)participant.ScreenIndexIsPinnedToFb); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + analogIndex += maxAnalogs; + } + + while (meetingIndex < maxParticipants * offset) + { + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); + tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, false); + tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, false); + tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, false); + tokenArray[digitalIndex + 5] = new XSigDigitalToken(digitalIndex + 6, false); + tokenArray[digitalIndex + 6] = new XSigDigitalToken(digitalIndex + 7, false); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); + + //analogs + tokenArray[analogIndex] = new XSigAnalogToken(analogIndex + 1, 0); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + analogIndex += maxAnalogs; + } + + var returnString = GetXSigString(tokenArray); + + //Debug.LogMessage(LogEventLevel.Verbose, this, "{0}", ComTextHelper.GetEscapedText(Encoding.GetEncoding(28591).GetBytes(returnString))); + + + return returnString; + } + + private void LinkVideoCodecContentSharingToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + SharingContentIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SourceShareStart.JoinNumber]); + SharingContentIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.SourceShareEnd.JoinNumber]); + + SharingSourceFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentSource.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.SourceShareStart.JoinNumber, StartSharing); + trilist.SetSigFalseAction(joinMap.SourceShareEnd.JoinNumber, StopSharing); + + trilist.SetBoolSigAction(joinMap.SourceShareAutoStart.JoinNumber, b => AutoShareContentWhileInCall = b); + } + + private List _currentMeetings = new List(); + + private void LinkVideoCodecScheduleToApi(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.UpdateMeetings.JoinNumber, codec.GetSchedule); + + trilist.SetUShortSigAction(joinMap.MinutesBeforeMeetingStart.JoinNumber, (i) => + { + codec.CodecSchedule.MeetingWarningMinutes = i; + }); + + + for (uint i = 0; i < joinMap.DialMeetingStart.JoinSpan; i++) + { + Debug.LogMessage(LogEventLevel.Debug, this, "Setting action to Dial Meeting {0} to digital join {1}", i + 1, joinMap.DialMeetingStart.JoinNumber + i); + var joinNumber = joinMap.DialMeetingStart.JoinNumber + i; + var mtg = i + 1; + var index = (int)i; + + trilist.SetSigFalseAction(joinNumber, () => + { + Debug.LogMessage(LogEventLevel.Debug, this, "Meeting {0} Selected (EISC dig-o{1}) > _currentMeetings[{2}].Id: {3}, Title: {4}", + mtg, joinMap.DialMeetingStart.JoinNumber + i, index, _currentMeetings[index].Id, _currentMeetings[index].Title); + if (_currentMeetings[index] != null) + Dial(_currentMeetings[index]); + }); + } + + codec.CodecSchedule.MeetingsListHasChanged += (sender, args) => UpdateMeetingsList(codec, trilist, joinMap); + codec.CodecSchedule.MeetingEventChange += (sender, args) => + { + if (args.ChangeType == eMeetingEventChangeType.MeetingStartWarning) + { + UpdateMeetingsList(codec, trilist, joinMap); + } + }; + + trilist.SetUShortSigAction(joinMap.MeetingsToDisplay.JoinNumber, m => MeetingsToDisplay = m); + MeetingsToDisplayFeedback.LinkInputSig(trilist.UShortInput[joinMap.MeetingsToDisplay.JoinNumber]); + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + // TODO [ ] Issue #868 + trilist.SetString(joinMap.Schedule.JoinNumber, "\xFC"); + UpdateMeetingsList(codec, trilist, joinMap); + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + MeetingsToDisplayFeedback.LinkInputSig(trilist.UShortInput[joinMap.MeetingsToDisplay.JoinNumber]); + }; + } + + private void UpdateMeetingsList(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + var currentTime = DateTime.Now; + + _currentMeetings = codec.CodecSchedule.Meetings.Where(m => m.StartTime >= currentTime || m.EndTime >= currentTime).ToList(); + + if (_currentMeetings.Count == 0) + { + var emptyXSigByteArray = XSigHelpers.ClearOutputs(); + var emptyXSigString = Encoding.GetEncoding(XSigEncoding) + .GetString(emptyXSigByteArray, 0, emptyXSigByteArray.Length); + + trilist.SetString(joinMap.Schedule.JoinNumber, emptyXSigString); + return; + } + + var meetingsData = UpdateMeetingsListXSig(_currentMeetings); + trilist.SetString(joinMap.Schedule.JoinNumber, meetingsData); + trilist.SetUshort(joinMap.MeetingCount.JoinNumber, (ushort)_currentMeetings.Count); + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + // TODO [ ] Issue #868 + trilist.SetString(joinMap.Schedule.JoinNumber, "\xFC"); + UpdateMeetingsListXSig(_currentMeetings); + }; + } + + + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + private int _meetingsToDisplay = 3; + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + protected int MeetingsToDisplay + { + get { return _meetingsToDisplay; } + set + { + _meetingsToDisplay = (ushort)(value == 0 ? 3 : value); + MeetingsToDisplayFeedback.FireUpdate(); + } + } + + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + public IntFeedback MeetingsToDisplayFeedback { get; set; } + + private string UpdateMeetingsListXSig(List meetings) + { + // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set + //const int _meetingsToDisplay = 3; + const int maxDigitals = 2; + const int maxStrings = 7; + const int offset = maxDigitals + maxStrings; + var digitalIndex = maxStrings * _meetingsToDisplay; //15 + var stringIndex = 0; + var meetingIndex = 0; + + var tokenArray = new XSigToken[_meetingsToDisplay * offset]; + /* + * Digitals + * IsJoinable - 1 + * IsDialable - 2 + * + * Serials + * Organizer - 1 + * Title - 2 + * Start Date - 3 + * Start Time - 4 + * End Date - 5 + * End Time - 6 + * Id - 7 + */ + + + foreach (var meeting in meetings) { var currentTime = DateTime.Now; - _currentMeetings = codec.CodecSchedule.Meetings.Where(m => m.StartTime >= currentTime || m.EndTime >= currentTime).ToList(); + if (meeting.StartTime < currentTime && meeting.EndTime < currentTime) continue; - if (_currentMeetings.Count == 0) - { - var emptyXSigByteArray = XSigHelpers.ClearOutputs(); - var emptyXSigString = Encoding.GetEncoding(XSigEncoding) - .GetString(emptyXSigByteArray, 0, emptyXSigByteArray.Length); - - trilist.SetString(joinMap.Schedule.JoinNumber, emptyXSigString); - return; - } - - var meetingsData = UpdateMeetingsListXSig(_currentMeetings); - trilist.SetString(joinMap.Schedule.JoinNumber, meetingsData); - trilist.SetUshort(joinMap.MeetingCount.JoinNumber, (ushort)_currentMeetings.Count); - - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; - - // TODO [ ] Issue #868 - trilist.SetString(joinMap.Schedule.JoinNumber, "\xFC"); - UpdateMeetingsListXSig(_currentMeetings); - }; - } - - - // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set - private int _meetingsToDisplay = 3; - // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set - protected int MeetingsToDisplay - { - get { return _meetingsToDisplay; } - set { - _meetingsToDisplay = (ushort) (value == 0 ? 3 : value); - MeetingsToDisplayFeedback.FireUpdate(); - } - } - - // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set - public IntFeedback MeetingsToDisplayFeedback { get; set; } - - private string UpdateMeetingsListXSig(List meetings) - { - // TODO [ ] hotfix/videocodecbase-max-meeting-xsig-set - //const int _meetingsToDisplay = 3; - const int maxDigitals = 2; - const int maxStrings = 7; - const int offset = maxDigitals + maxStrings; - var digitalIndex = maxStrings * _meetingsToDisplay; //15 - var stringIndex = 0; - var meetingIndex = 0; - - var tokenArray = new XSigToken[_meetingsToDisplay * offset]; - /* - * Digitals - * IsJoinable - 1 - * IsDialable - 2 - * - * Serials - * Organizer - 1 - * Title - 2 - * Start Date - 3 - * Start Time - 4 - * End Date - 5 - * End Time - 6 - * Id - 7 - */ - - - foreach (var meeting in meetings) - { - var currentTime = DateTime.Now; - - if (meeting.StartTime < currentTime && meeting.EndTime < currentTime) continue; - - if (meetingIndex >= _meetingsToDisplay * offset) - { - Debug.LogMessage(LogEventLevel.Verbose, this, "Max Meetings reached"); - break; - } - - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, meeting.Joinable); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, meeting.Dialable); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, meeting.Organizer); - tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, meeting.Title); - tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, meeting.StartTime.ToString(_dateFormatSpecifier.NullIfEmpty() ?? "d", Global.Culture)); - tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, meeting.StartTime.ToString(_timeFormatSpecifier.NullIfEmpty() ?? "t", Global.Culture)); - tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, meeting.EndTime.ToString(_dateFormatSpecifier.NullIfEmpty() ?? "d", Global.Culture)); - tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, meeting.EndTime.ToString(_timeFormatSpecifier.NullIfEmpty() ?? "t", Global.Culture)); - tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, meeting.Id); - - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - } - - while (meetingIndex < _meetingsToDisplay * offset) - { - Debug.LogMessage(LogEventLevel.Verbose, this, "Clearing unused data. Meeting Index: {0} MaxMeetings * Offset: {1}", - meetingIndex, _meetingsToDisplay * offset); - - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); - tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty); - tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty); - tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty); - tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty); - tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty); - tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, String.Empty); - - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - } - - return GetXSigString(tokenArray); - } - - - private void LinkVideoCodecDirectoryToApi(IHasDirectory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.CurrentDirectoryResultIsNotDirectoryRoot.LinkComplementInputSig( - trilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]); - - trilist.SetStringSigAction(joinMap.DirectorySearchString.JoinNumber, codec.SearchDirectory); - - trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i, trilist, joinMap)); - - //Special Change for protected directory clear - - trilist.SetBoolSigAction(joinMap.DirectoryClearSelected.JoinNumber, (b) => SelectDirectoryEntry(_directoryCodec, 0, _directoryTrilist, _directoryJoinmap)); - - // Report feedback for number of contact methods for selected contact - - trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); - - trilist.SetSigFalseAction(joinMap.DirectoryFolderBack.JoinNumber, codec.GetDirectoryParentFolderContents); - - if (codec.DirectoryRoot != null) - { - var contactsCount = codec.DirectoryRoot.CurrentDirectoryResults.Where(c => c.ParentFolderId.Equals("root")).ToList().Count; - trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)contactsCount); - Debug.LogMessage(LogEventLevel.Verbose, this, ">>> contactsCount: {0}", contactsCount); - - var clearBytes = XSigHelpers.ClearOutputs(); - - trilist.SetString(joinMap.DirectoryEntries.JoinNumber, - Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); - var directoryXSig = UpdateDirectoryXSig(codec.DirectoryRoot, - codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false); - - Debug.LogMessage(LogEventLevel.Verbose, this, "Directory XSig Length: {0}", directoryXSig.Length); - - trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); - } - - codec.DirectoryResultReturned += (sender, args) => + if (meetingIndex >= _meetingsToDisplay * offset) { - 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.LogMessage(LogEventLevel.Verbose, this, ">>> argsCount: {0}", argsCount); - - var clearBytes = XSigHelpers.ClearOutputs(); - - trilist.SetString(joinMap.DirectoryEntries.JoinNumber, - Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); - var directoryXSig = UpdateDirectoryXSig(args.Directory, - codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false); - Debug.LogMessage(LogEventLevel.Verbose, this, "Directory XSig Length: {0}", directoryXSig.Length); - - trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); - }; - - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; - - var clearBytes = XSigHelpers.ClearOutputs(); - trilist.SetString(joinMap.DirectoryEntries.JoinNumber, - Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); - var directoryXSig = UpdateDirectoryXSig(codec.DirectoryRoot, codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false); - trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); - }; - } - - private void SelectDirectoryEntry(IHasDirectory codec, ushort i, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - if (i > codec.CurrentDirectoryResult.CurrentDirectoryResults.Count) return; - _selectedDirectoryItem = i == 0 ? null : codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1]; - trilist.SetUshort(joinMap.DirectorySelectRowFeedback.JoinNumber, i); - - if (_selectedDirectoryItem == null) trilist.SetBool(joinMap.DirectoryEntryIsContact.JoinNumber, false); - - - if (_selectedDirectoryItem is DirectoryFolder) - { - codec.GetDirectoryFolderContents(_selectedDirectoryItem.FolderId); - trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, 0); - trilist.SetString(joinMap.DirectorySelectedFolderName.JoinNumber, _selectedDirectoryItem.Name); - trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, string.Empty); - trilist.ClearUShortSigAction(joinMap.SelectContactMethod.JoinNumber); - trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedLine.JoinNumber); - trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber); - trilist.SetBool(joinMap.DirectoryEntryIsContact.JoinNumber, false); - return; + Debug.LogMessage(LogEventLevel.Verbose, this, "Max Meetings reached"); + break; } - // not a folder. Clear this value - trilist.SetString(joinMap.DirectorySelectedFolderName.JoinNumber, string.Empty); + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, meeting.Joinable); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, meeting.Dialable); - var selectedContact = _selectedDirectoryItem as DirectoryContact; + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, meeting.Organizer); + tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, meeting.Title); + tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, meeting.StartTime.ToString(_dateFormatSpecifier.NullIfEmpty() ?? "d", Global.Culture)); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, meeting.StartTime.ToString(_timeFormatSpecifier.NullIfEmpty() ?? "t", Global.Culture)); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, meeting.EndTime.ToString(_dateFormatSpecifier.NullIfEmpty() ?? "d", Global.Culture)); + tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, meeting.EndTime.ToString(_timeFormatSpecifier.NullIfEmpty() ?? "t", Global.Culture)); + tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, meeting.Id); - if (selectedContact != null && selectedContact.ContactMethods.Count >= 1) - { - trilist.SetBool(joinMap.DirectoryEntryIsContact.JoinNumber, true); - } - - trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, - selectedContact != null ? selectedContact.Name : string.Empty); - - // Allow auto dial of selected line. Always dials first contact method - if (!trilist.GetBool(joinMap.DirectoryDisableAutoDialSelectedLine.JoinNumber)) - { - if (_selectedDirectoryItem is IInvitableContact invitableEntry) - { - Dial(invitableEntry); - return; - } - - - trilist.SetString(joinMap.DirectoryEntrySelectedNumber.JoinNumber, - selectedContact != null ? selectedContact.ContactMethods[0].Number : string.Empty); - - if (!(_selectedDirectoryItem is DirectoryContact entryToDial)) return; - - Dial(entryToDial.ContactMethods[0].Number); - } - else - { - // If auto dial is disabled... - - if (!(_selectedDirectoryItem is DirectoryContact entryToDial)) - { - // Clear out values and actions from last selected item - trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, 0); - trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, string.Empty); - trilist.ClearUShortSigAction(joinMap.SelectContactMethod.JoinNumber); - trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedLine.JoinNumber); - trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber); - return; - } - - trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, (ushort)entryToDial.ContactMethods.Count); - - // Update the action to dial the selected contact method - trilist.SetUShortSigAction(joinMap.SelectContactMethod.JoinNumber, (u) => - { - if (u < 1 || u > entryToDial.ContactMethods.Count) return; - - trilist.SetSigFalseAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber, () => Dial(entryToDial.ContactMethods[u - 1].Number)); - }); - - // Sets DirectoryDialSelectedLine join action to dial first contact method - trilist.SetSigFalseAction(joinMap.DirectoryDialSelectedLine.JoinNumber, () => Dial(entryToDial.ContactMethods[0].Number)); - - var clearBytes = XSigHelpers.ClearOutputs(); - - trilist.SetString(joinMap.ContactMethods.JoinNumber, - Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); - var contactMethodsXSig = UpdateContactMethodsXSig(entryToDial); - - trilist.SetString(joinMap.ContactMethods.JoinNumber, contactMethodsXSig); - } + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; } - /// - /// Generates the XSig data representing the available contact methods for the selected DirectoryContact - /// - /// - /// - private string UpdateContactMethodsXSig(DirectoryContact contact) - { - const int maxMethods = 10; - const int maxStrings = 3; - const int offset = maxStrings; - var stringIndex = 0; - var arrayIndex = 0; - // Create a new token array and set the size to the number of methods times the total number of signals - var tokenArray = new XSigToken[maxMethods * offset]; - - Debug.LogMessage(LogEventLevel.Verbose, this, "Creating XSIG token array with size {0}", maxMethods * offset); - - // TODO: Add code to generate XSig data - foreach (var method in contact.ContactMethods) - { - if (arrayIndex >= maxMethods * offset) - break; - - //serials - tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, method.Number); - tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, method.ContactMethodId.ToString()); - tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, method.Device.ToString()); - - arrayIndex += offset; - stringIndex += maxStrings; - } - - while (arrayIndex < maxMethods) - { - tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty); - tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty); - tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty); - - arrayIndex += offset; - stringIndex += maxStrings; - } - - return GetXSigString(tokenArray); - } - - private string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot) + while (meetingIndex < _meetingsToDisplay * offset) { - var xSigMaxIndex = 1023; - var tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count > xSigMaxIndex - ? xSigMaxIndex - : directory.CurrentDirectoryResults.Count]; + Debug.LogMessage(LogEventLevel.Verbose, this, "Clearing unused data. Meeting Index: {0} MaxMeetings * Offset: {1}", + meetingIndex, _meetingsToDisplay * offset); - Debug.LogMessage(LogEventLevel.Verbose, this, "IsRoot: {0}, Directory Count: {1}, TokenArray.Length: {2}", isRoot, directory.CurrentDirectoryResults.Count, tokenArray.Length); + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); - var contacts = directory.CurrentDirectoryResults.Count > xSigMaxIndex - ? directory.CurrentDirectoryResults.Take(xSigMaxIndex) - : directory.CurrentDirectoryResults; + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); + tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty); + tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty); + tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty); + tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, String.Empty); - var contactsToDisplay = isRoot - ? contacts.Where(c => c.ParentFolderId == "root") - : contacts.Where(c => c.ParentFolderId != "root"); - - var counterIndex = 1; - foreach (var entry in contactsToDisplay) - { - var arrayIndex = counterIndex - 1; - var entryIndex = counterIndex; - - Debug.LogMessage(LogEventLevel.Verbose, this, "Entry{2:0000} Name: {0}, Folder ID: {1}, Type: {3}, ParentFolderId: {4}", - entry.Name, entry.FolderId, entryIndex, entry.GetType().FullName, entry.ParentFolderId); - - if (entry is DirectoryFolder) - { - tokenArray[arrayIndex] = new XSigSerialToken(entryIndex, String.Format("[+] {0}", entry.Name)); - - counterIndex++; - - continue; - } - - tokenArray[arrayIndex] = new XSigSerialToken(entryIndex, entry.Name); - - counterIndex++; - } - - return GetXSigString(tokenArray); + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; } - private void LinkVideoCodecCallControlsToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.ManualDial.JoinNumber, - () => Dial(trilist.StringOutput[joinMap.CurrentDialString.JoinNumber].StringValue)); - - //End All calls - trilist.SetSigFalseAction(joinMap.EndAllCalls.JoinNumber, EndAllCalls); - - //End a specific call, specified by index. Maximum 8 calls supported - for (int i = 0; i < joinMap.EndCallStart.JoinSpan; i++) - { - var callIndex = i; - - trilist.SetSigFalseAction((uint)(joinMap.EndCallStart.JoinNumber + i), () => - { - - if (callIndex < 0 || callIndex >= ActiveCalls.Count) - { - Debug.LogMessage(LogEventLevel.Verbose, this, "Cannot end call. No call found at index: {0}", callIndex); - return; - } - - var call = ActiveCalls[callIndex]; - if (call != null) - { - EndCall(call); - } - else - { - Debug.LogMessage(LogEventLevel.Information, this, "[End Call] Unable to find call at index '{0}'", i); - } - }); - } - - trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); - - CallStatusChange += (sender, args) => - { - trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); - - Debug.LogMessage(LogEventLevel.Debug, this, "Call Direction: {0}", args.CallItem.Direction); - Debug.LogMessage(LogEventLevel.Debug, this, "Call is incoming: {0}", args.CallItem.Direction == eCodecCallDirection.Incoming); - trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status == eCodecCallStatus.Ringing); - - if (args.CallItem.Direction == eCodecCallDirection.Incoming) - { - trilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem)); - trilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem)); - trilist.SetString(joinMap.IncomingCallName.JoinNumber, args.CallItem.Name); - trilist.SetString(joinMap.IncomingCallNumber.JoinNumber, args.CallItem.Number); - } - else - { - trilist.SetString(joinMap.IncomingCallName.JoinNumber, string.Empty); - trilist.SetString(joinMap.IncomingCallNumber.JoinNumber, string.Empty); - } - - - trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); - - trilist.SetUshort(joinMap.ConnectedCallCount.JoinNumber, (ushort)ActiveCalls.Count); - }; - - if (this is IJoinCalls joinCodec) - { - trilist.SetSigFalseAction(joinMap.JoinAllCalls.JoinNumber, () => joinCodec.JoinAllCalls()); - - for (int i = 0; i < joinMap.JoinCallStart.JoinSpan; i++) - { - trilist.SetSigFalseAction((uint)(joinMap.JoinCallStart.JoinNumber + i), () => - { - var call = ActiveCalls[i]; - if (call != null) - { - joinCodec.JoinCall(call); - } - else - { - Debug.LogMessage(LogEventLevel.Information, this, "[Join Call] Unable to find call at index '{0}'", i); - } - }); - } - } - - if (this is IHasCallHold holdCodec) - { - trilist.SetSigFalseAction(joinMap.HoldAllCalls.JoinNumber, () => - { - foreach (var call in ActiveCalls) - { - holdCodec.HoldCall(call); - } - }); - - for (int i = 0; i < joinMap.HoldCallsStart.JoinSpan; i++) - { - var index = i; - - trilist.SetSigFalseAction((uint)(joinMap.HoldCallsStart.JoinNumber + index), () => - { - if (index < 0 || index >= ActiveCalls.Count) return; - - var call = ActiveCalls[index]; - if (call != null) - { - holdCodec.HoldCall(call); - } - else - { - Debug.LogMessage(LogEventLevel.Information, this, "[Hold Call] Unable to find call at index '{0}'", i); - } - }); - - trilist.SetSigFalseAction((uint)(joinMap.ResumeCallsStart.JoinNumber + index), () => - { - if (index < 0 || index >= ActiveCalls.Count) return; - - var call = ActiveCalls[index]; - if (call != null) - { - holdCodec.ResumeCall(call); - } - else - { - Debug.LogMessage(LogEventLevel.Information, this, "[Resume Call] Unable to find call at index '{0}'", i); - } - }); - } - } - - - - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; - - // TODO [ ] #983 - Debug.LogMessage(LogEventLevel.Information, this, "LinkVideoCodecCallControlsToApi: device is {0}, IsInCall {1}", args.DeviceOnLine ? "online" : "offline", IsInCall); - trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); - trilist.SetString(joinMap.CurrentCallData.JoinNumber, "\xFC"); - trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); - }; - } - - private string UpdateCallStatusXSig() - { - const int maxCalls = 8; - const int maxStrings = 6; - const int maxDigitals = 2; - const int offset = maxStrings + maxDigitals; - var stringIndex = 0; - var digitalIndex = maxStrings * maxCalls; - var arrayIndex = 0; - - var tokenArray = new XSigToken[maxCalls * offset]; //set array size for number of calls * pieces of info - - foreach (var call in ActiveCalls) - { - if (arrayIndex >= maxCalls * offset) - break; - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, call.IsOnHold); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, call.Name ?? String.Empty); - tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, call.Number ?? String.Empty); - tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, call.Direction.ToString()); - tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, call.Type.ToString()); - tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, call.Status.ToString()); - if(call.Duration != null) - { - // May need to verify correct string format here - var dur = string.Format("{0:c}", call.Duration); - tokenArray[arrayIndex + 6] = new XSigSerialToken(stringIndex + 6, dur); - } - - arrayIndex += offset; - stringIndex += maxStrings; - digitalIndex += maxDigitals; - } - while (arrayIndex < maxCalls * offset) - { - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); - - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); - tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty); - tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty); - tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty); - tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty); - tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty); - - arrayIndex += offset; - stringIndex += maxStrings; - digitalIndex += maxDigitals; - } - - return GetXSigString(tokenArray); - } - - private void LinkVideoCodecDtmfToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmfAction("0", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmfAction("1", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmfAction("2", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmfAction("3", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmfAction("4", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmfAction("5", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmfAction("6", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmfAction("7", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmfAction("8", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmfAction("9", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmfAction("*", trilist, joinMap)); - trilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmfAction("#", trilist, joinMap)); - } - - /// - /// Sends the specified string as a DTMF command. - /// Reads the value of the SendDtmfToSpecificCallInstance digital join and SelectCall analog join to determine - /// Whther to send to a specific call index or to the last connected call - /// - /// - /// - /// - private void SendDtmfAction(string s, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - if (!trilist.GetBool(joinMap.SendDtmfToSpecificCallIndex.JoinNumber)) - { - SendDtmf(s); - } - else - { - var callIndex = trilist.GetUshort(joinMap.SelectCall.JoinNumber); - if (callIndex > 0 && callIndex <= 8) - { - var call = ActiveCalls[callIndex - 1]; - if (call != null && call.IsActiveCall) - { - SendDtmf(s, call); - } - else - { - Debug.LogMessage(LogEventLevel.Information, this, "Warning: No call found at index {0} or call is not active.", callIndex); - } - } - else - { - Debug.LogMessage(LogEventLevel.Information, this, "Warning: Invalid call index specified. Please use a value of 1-8."); - } - } - } - - private void LinkVideoCodecCameraLayoutsToApi(IHasCodecLayouts codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraLayout.JoinNumber, codec.LocalLayoutToggle); - - codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentLayoutStringFb.JoinNumber]); - } - - private void LinkVideoCodecCameraModeToApi(IHasCameraAutoMode codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeOn); - trilist.SetSigFalseAction(joinMap.CameraModeManual.JoinNumber, codec.CameraAutoModeOff); - - codec.CameraAutoModeIsOnFeedback.OutputChange += (o, a) => - { - if (codec is IHasCameraOff offCodec) - { - if (offCodec.CameraIsOffFeedback.BoolValue) - { - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - }; - - - if (codec is IHasCameraOff offModeCodec) - { - if (offModeCodec.CameraIsOffFeedback.BoolValue) - { - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - } - - private void LinkVideoCodecSelfviewToApi(IHasCodecSelfView codec, BasicTriList trilist, - VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraSelfView.JoinNumber, codec.SelfViewModeToggle); - - codec.SelfviewIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.CameraSelfView.JoinNumber]); - } - - private void LinkVideoCodecCameraToApi(IHasCodecCameras codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - //Camera PTZ - trilist.SetBoolSigAction(joinMap.CameraTiltUp.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - - if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; - - if (b) camera.TiltUp(); - else camera.TiltStop(); - }); - - trilist.SetBoolSigAction(joinMap.CameraTiltDown.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - - if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; - - if (b) camera.TiltDown(); - else camera.TiltStop(); - }); - trilist.SetBoolSigAction(joinMap.CameraPanLeft.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - - if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; - - if (b) camera.PanLeft(); - else camera.PanStop(); - }); - trilist.SetBoolSigAction(joinMap.CameraPanRight.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - - if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; - - if (b) camera.PanRight(); - else camera.PanStop(); - }); - - trilist.SetBoolSigAction(joinMap.CameraZoomIn.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - - if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; - - if (b) camera.ZoomIn(); - else camera.ZoomStop(); - }); - - trilist.SetBoolSigAction(joinMap.CameraZoomOut.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - - if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; - - if (b) camera.ZoomOut(); - else camera.ZoomStop(); - }); - - - trilist.SetBoolSigAction(joinMap.CameraFocusNear.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - - if (!(codec.SelectedCamera is IHasCameraFocusControl camera)) return; - - if (b) camera.FocusNear(); - else camera.FocusStop(); - }); - - trilist.SetBoolSigAction(joinMap.CameraFocusFar.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - - if (!(codec.SelectedCamera is IHasCameraFocusControl camera)) return; - - if (b) camera.FocusFar(); - else camera.FocusStop(); - }); - - trilist.SetSigFalseAction(joinMap.CameraFocusAuto.JoinNumber, () => - { - if (codec.SelectedCamera == null) return; - - if (!(codec.SelectedCamera is IHasCameraFocusControl camera)) return; - - camera.TriggerAutoFocus(); - }); - - // Camera count - trilist.SetUshort(joinMap.CameraCount.JoinNumber, (ushort)codec.Cameras.Count); - - // Camera names - for (uint i = 0; i < joinMap.CameraNamesFb.JoinSpan; i++) - { - //Check the count first - if (i < codec.Cameras.Count && codec.Cameras[(int)i] != null) - { - trilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, codec.Cameras[(int)i].Name); - } - else - { - trilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, ""); - } - } - - //Camera Select - trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) => - { - if (i > 0 && i <= codec.Cameras.Count) - { - codec.SelectCamera(codec.Cameras[i - 1].Key); - } - else - { - Debug.LogMessage(LogEventLevel.Information, this, "Unable to select. No camera found at index {0}", i); - } - }); - - // Set initial selected camera feedback - if (codec.SelectedCamera != null) - { - trilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)codec.Cameras.FindIndex((c) => c.Key == codec.SelectedCamera.Key)); - } - - codec.CameraSelected += (sender, args) => - { - var i = (ushort)codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key); - - trilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)(i + 1)); - - if (codec is IHasCodecRoomPresets) - { - return; - } - - if (!(args.SelectedCamera is IHasCameraPresets)) - { - return; - } - - var cam = args.SelectedCamera as IHasCameraPresets; - SetCameraPresetNames(cam.Presets); - - (args.SelectedCamera as IHasCameraPresets).PresetsListHasChanged += (o, eventArgs) => SetCameraPresetNames(cam.Presets); - - trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, - (a) => - { - cam.PresetSelect(a); - trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, a); - }); - - trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, - () => - { - cam.PresetStore(trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, - String.Empty); - trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); - }); - }; - - if (!(codec is IHasCodecRoomPresets)) return; - - var presetCodec = codec as IHasCodecRoomPresets; - - presetCodec.CodecRoomPresetsListHasChanged += - (sender, args) => SetCameraPresetNames(presetCodec.NearEndPresets); - - //Camera Presets - trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, (i) => - { - presetCodec.CodecRoomPresetSelect(i); - }); - - - // Far End Presets - trilist.SetUShortSigAction(joinMap.FarEndPresetSelect.JoinNumber, (i) => - { - presetCodec.SelectFarEndPreset(i); - }); - - - trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, - () => - { - presetCodec.CodecRoomPresetStore( - trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, String.Empty); - trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); - }); - - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; - - // TODO [ ] Issue #868 - trilist.SetString(joinMap.CameraPresetNames.JoinNumber, "\xFC"); - SetCameraPresetNames(presetCodec.NearEndPresets); - }; - } - - // Following fields only used for Bridging - private int _selectedRecentCallItemIndex; - private DirectoryItem _selectedDirectoryItem; - - private void LinkVideoCodecCallHistoryToApi(IHasCallHistory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.CallHistory.RecentCallsListHasChanged += (o, a) => - { - UpdateCallHistory(codec, trilist, joinMap); - }; - - // Selected item action and feedback - trilist.SetUShortSigAction(joinMap.SelectRecentCallItem.JoinNumber, (u) => - { - if (u == 0 || u > codec.CallHistory.RecentCalls.Count) - { - Debug.LogMessage(LogEventLevel.Verbose, this, "Recent Call History index out of range"); - return; - } - - _selectedRecentCallItemIndex = (int)(u - 1); - trilist.SetUshort(joinMap.SelectRecentCallItem.JoinNumber, u); - - var _selectedRecentCallItem = codec.CallHistory.RecentCalls[_selectedRecentCallItemIndex]; - - if (_selectedRecentCallItem != null) - { - trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, _selectedRecentCallItem.Name); - trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, _selectedRecentCallItem.Number); - trilist.SetSigFalseAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber, () => codec.RemoveCallHistoryEntry(_selectedRecentCallItem)); - trilist.SetSigFalseAction(joinMap.DialSelectedRecentCallItem.JoinNumber, () => this.Dial(_selectedRecentCallItem.Number)); - } - else - { - trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, string.Empty); - trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, string.Empty); - trilist.ClearBoolSigAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber); - trilist.ClearBoolSigAction(joinMap.DialSelectedRecentCallItem.JoinNumber); - } - }); - } - - - - private void UpdateCallHistory(IHasCallHistory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - // Clear out selected item - _selectedRecentCallItemIndex = 0; - - trilist.SetUshort(joinMap.SelectRecentCallItem.JoinNumber, 0); - trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, string.Empty); - trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, string.Empty); - trilist.ClearBoolSigAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber); - // - - trilist.SetUshort(joinMap.RecentCallCount.JoinNumber, (ushort)codec.CallHistory.RecentCalls.Count); - - // Update the call history joins - var maxItems = joinMap.RecentCallNamesStart.JoinSpan; - - // Create history - uint index = 0; - for (uint i = 0; i < maxItems && i < codec.CallHistory.RecentCalls.Count; i++) - { - trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + i, codec.CallHistory.RecentCalls[(int)i].Name); - trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + i, codec.CallHistory.RecentCalls[(int)i].StartTime.ToShortTimeString()); - trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + i, (ushort)codec.CallHistory.RecentCalls[(int)i].OccurrenceType); - //i++; - index = i; - } - - //foreach(var item in codec.CallHistory.RecentCalls) - //{ - // trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + i, item.Name); - // trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + i, item.StartTime.ToShortTimeString()); - // trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + i, (ushort)item.OccurrenceType); - // i++; - //} - - // Clears existing items - for (uint j = index; j < maxItems; j++) - { - trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + j, string.Empty); - trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + j, string.Empty); - trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + j, 0); - } - } - - private string SetCameraPresetNames(IEnumerable presets) - { - return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); - } - - private string SetCameraPresetNames(IEnumerable presets) - { - return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); - } - - private string SetCameraPresetNames(ICollection presets) - { - var i = 1; //start index for xsig; - - var tokenArray = new XSigToken[presets.Count]; - - foreach (var preset in presets) - { - var cameraPreset = new XSigSerialToken(i, preset); - tokenArray[i - 1] = cameraPreset; - i++; - } - - return GetXSigString(tokenArray); - } - - private string GetXSigString(XSigToken[] tokenArray) - { - string returnString; - using (var s = new MemoryStream()) - { - using (var tw = new XSigTokenStreamWriter(s, true)) - { - tw.WriteXSigData(tokenArray); - } - - var xSig = s.ToArray(); - - returnString = Encoding.GetEncoding(XSigEncoding).GetString(xSig, 0, xSig.Length); - } - - return returnString; - } - - #endregion + return GetXSigString(tokenArray); } - /// - /// Used to track the status of syncronizing the phonebook values when connecting to a codec or refreshing the phonebook info - /// - public class CodecPhonebookSyncState : IKeyed + private void LinkVideoCodecDirectoryToApi(IHasDirectory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) { - private bool _InitialSyncComplete; + codec.CurrentDirectoryResultIsNotDirectoryRoot.LinkComplementInputSig( + trilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]); - public CodecPhonebookSyncState(string key) + trilist.SetStringSigAction(joinMap.DirectorySearchString.JoinNumber, codec.SearchDirectory); + + trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i, trilist, joinMap)); + + //Special Change for protected directory clear + + trilist.SetBoolSigAction(joinMap.DirectoryClearSelected.JoinNumber, (b) => SelectDirectoryEntry(_directoryCodec, 0, _directoryTrilist, _directoryJoinmap)); + + // Report feedback for number of contact methods for selected contact + + trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); + + trilist.SetSigFalseAction(joinMap.DirectoryFolderBack.JoinNumber, codec.GetDirectoryParentFolderContents); + + if (codec.DirectoryRoot != null) { - Key = key; + var contactsCount = codec.DirectoryRoot.CurrentDirectoryResults.Where(c => c.ParentFolderId.Equals("root")).ToList().Count; + trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)contactsCount); + Debug.LogMessage(LogEventLevel.Verbose, this, ">>> contactsCount: {0}", contactsCount); - CodecDisconnected(); + var clearBytes = XSigHelpers.ClearOutputs(); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, + Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); + var directoryXSig = UpdateDirectoryXSig(codec.DirectoryRoot, + codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false); + + Debug.LogMessage(LogEventLevel.Verbose, this, "Directory XSig Length: {0}", directoryXSig.Length); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); } - public bool InitialSyncComplete + codec.DirectoryResultReturned += (sender, args) => { - get { return _InitialSyncComplete; } - private set + 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.LogMessage(LogEventLevel.Verbose, this, ">>> argsCount: {0}", argsCount); + + var clearBytes = XSigHelpers.ClearOutputs(); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, + Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); + var directoryXSig = UpdateDirectoryXSig(args.Directory, + codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false); + Debug.LogMessage(LogEventLevel.Verbose, this, "Directory XSig Length: {0}", directoryXSig.Length); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); + }; + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + var clearBytes = XSigHelpers.ClearOutputs(); + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, + Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); + var directoryXSig = UpdateDirectoryXSig(codec.DirectoryRoot, codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue == false); + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); + }; + } + + private void SelectDirectoryEntry(IHasDirectory codec, ushort i, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + if (i > codec.CurrentDirectoryResult.CurrentDirectoryResults.Count) return; + _selectedDirectoryItem = i == 0 ? null : codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1]; + trilist.SetUshort(joinMap.DirectorySelectRowFeedback.JoinNumber, i); + + if (_selectedDirectoryItem == null) trilist.SetBool(joinMap.DirectoryEntryIsContact.JoinNumber, false); + + + if (_selectedDirectoryItem is DirectoryFolder) + { + codec.GetDirectoryFolderContents(_selectedDirectoryItem.FolderId); + trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, 0); + trilist.SetString(joinMap.DirectorySelectedFolderName.JoinNumber, _selectedDirectoryItem.Name); + trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, string.Empty); + trilist.ClearUShortSigAction(joinMap.SelectContactMethod.JoinNumber); + trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedLine.JoinNumber); + trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber); + trilist.SetBool(joinMap.DirectoryEntryIsContact.JoinNumber, false); + return; + } + + // not a folder. Clear this value + trilist.SetString(joinMap.DirectorySelectedFolderName.JoinNumber, string.Empty); + + var selectedContact = _selectedDirectoryItem as DirectoryContact; + + if (selectedContact != null && selectedContact.ContactMethods.Count >= 1) + { + trilist.SetBool(joinMap.DirectoryEntryIsContact.JoinNumber, true); + } + + trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, + selectedContact != null ? selectedContact.Name : string.Empty); + + // Allow auto dial of selected line. Always dials first contact method + if (!trilist.GetBool(joinMap.DirectoryDisableAutoDialSelectedLine.JoinNumber)) + { + if (_selectedDirectoryItem is IInvitableContact invitableEntry) { - if (value == true) - { - InitialSyncCompleted?.Invoke(this, new EventArgs()); - } - _InitialSyncComplete = value; + Dial(invitableEntry); + return; } + + + trilist.SetString(joinMap.DirectoryEntrySelectedNumber.JoinNumber, + selectedContact != null ? selectedContact.ContactMethods[0].Number : string.Empty); + + if (!(_selectedDirectoryItem is DirectoryContact entryToDial)) return; + + Dial(entryToDial.ContactMethods[0].Number); } - - public bool InitialPhonebookFoldersWasReceived { get; private set; } - - public bool NumberOfContactsWasReceived { get; private set; } - - public bool PhonebookRootEntriesWasRecieved { get; private set; } - - public bool PhonebookHasFolders { get; private set; } - - public int NumberOfContacts { get; private set; } - - #region IKeyed Members - - public string Key { get; private set; } - - #endregion - - public event EventHandler InitialSyncCompleted; - - public void InitialPhonebookFoldersReceived() + else { - InitialPhonebookFoldersWasReceived = true; + // If auto dial is disabled... - CheckSyncStatus(); - } - - public void PhonebookRootEntriesReceived() - { - PhonebookRootEntriesWasRecieved = true; - - CheckSyncStatus(); - } - - public void SetPhonebookHasFolders(bool value) - { - PhonebookHasFolders = value; - - Debug.LogMessage(LogEventLevel.Debug, this, "Phonebook has folders: {0}", PhonebookHasFolders); - } - - public void SetNumberOfContacts(int contacts) - { - NumberOfContacts = contacts; - NumberOfContactsWasReceived = true; - - Debug.LogMessage(LogEventLevel.Debug, this, "Phonebook contains {0} contacts.", NumberOfContacts); - - CheckSyncStatus(); - } - - public void CodecDisconnected() - { - InitialPhonebookFoldersWasReceived = false; - PhonebookHasFolders = false; - NumberOfContacts = 0; - NumberOfContactsWasReceived = false; - } - - private void CheckSyncStatus() - { - if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved) + if (!(_selectedDirectoryItem is DirectoryContact entryToDial)) { - InitialSyncComplete = true; - Debug.LogMessage(LogEventLevel.Debug, this, "Initial Phonebook Sync Complete!"); + // Clear out values and actions from last selected item + trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, 0); + trilist.SetString(joinMap.DirectoryEntrySelectedName.JoinNumber, string.Empty); + trilist.ClearUShortSigAction(joinMap.SelectContactMethod.JoinNumber); + trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedLine.JoinNumber); + trilist.ClearBoolSigAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber); + return; + } + + trilist.SetUshort(joinMap.SelectedContactMethodCount.JoinNumber, (ushort)entryToDial.ContactMethods.Count); + + // Update the action to dial the selected contact method + trilist.SetUShortSigAction(joinMap.SelectContactMethod.JoinNumber, (u) => + { + if (u < 1 || u > entryToDial.ContactMethods.Count) return; + + trilist.SetSigFalseAction(joinMap.DirectoryDialSelectedContactMethod.JoinNumber, () => Dial(entryToDial.ContactMethods[u - 1].Number)); + }); + + // Sets DirectoryDialSelectedLine join action to dial first contact method + trilist.SetSigFalseAction(joinMap.DirectoryDialSelectedLine.JoinNumber, () => Dial(entryToDial.ContactMethods[0].Number)); + + var clearBytes = XSigHelpers.ClearOutputs(); + + trilist.SetString(joinMap.ContactMethods.JoinNumber, + Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); + var contactMethodsXSig = UpdateContactMethodsXSig(entryToDial); + + trilist.SetString(joinMap.ContactMethods.JoinNumber, contactMethodsXSig); + } + } + + /// + /// Generates the XSig data representing the available contact methods for the selected DirectoryContact + /// + /// + /// + private string UpdateContactMethodsXSig(DirectoryContact contact) + { + const int maxMethods = 10; + const int maxStrings = 3; + const int offset = maxStrings; + var stringIndex = 0; + var arrayIndex = 0; + // Create a new token array and set the size to the number of methods times the total number of signals + var tokenArray = new XSigToken[maxMethods * offset]; + + Debug.LogMessage(LogEventLevel.Verbose, this, "Creating XSIG token array with size {0}", maxMethods * offset); + + // TODO: Add code to generate XSig data + foreach (var method in contact.ContactMethods) + { + if (arrayIndex >= maxMethods * offset) + break; + + //serials + tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, method.Number); + tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, method.ContactMethodId.ToString()); + tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, method.Device.ToString()); + + arrayIndex += offset; + stringIndex += maxStrings; + } + + while (arrayIndex < maxMethods) + { + tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty); + tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty); + tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty); + + arrayIndex += offset; + stringIndex += maxStrings; + } + + 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]; + + Debug.LogMessage(LogEventLevel.Verbose, 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 contactsToDisplay = isRoot + ? contacts.Where(c => c.ParentFolderId == "root") + : contacts.Where(c => c.ParentFolderId != "root"); + + var counterIndex = 1; + foreach (var entry in contactsToDisplay) + { + var arrayIndex = counterIndex - 1; + var entryIndex = counterIndex; + + Debug.LogMessage(LogEventLevel.Verbose, this, "Entry{2:0000} Name: {0}, Folder ID: {1}, Type: {3}, ParentFolderId: {4}", + entry.Name, entry.FolderId, entryIndex, entry.GetType().FullName, entry.ParentFolderId); + + if (entry is DirectoryFolder) + { + tokenArray[arrayIndex] = new XSigSerialToken(entryIndex, String.Format("[+] {0}", entry.Name)); + + counterIndex++; + + continue; + } + + tokenArray[arrayIndex] = new XSigSerialToken(entryIndex, entry.Name); + + counterIndex++; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecCallControlsToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.ManualDial.JoinNumber, + () => Dial(trilist.StringOutput[joinMap.CurrentDialString.JoinNumber].StringValue)); + + //End All calls + trilist.SetSigFalseAction(joinMap.EndAllCalls.JoinNumber, EndAllCalls); + + //End a specific call, specified by index. Maximum 8 calls supported + for (int i = 0; i < joinMap.EndCallStart.JoinSpan; i++) + { + var callIndex = i; + + trilist.SetSigFalseAction((uint)(joinMap.EndCallStart.JoinNumber + i), () => + { + + if (callIndex < 0 || callIndex >= ActiveCalls.Count) + { + Debug.LogMessage(LogEventLevel.Verbose, this, "Cannot end call. No call found at index: {0}", callIndex); + return; + } + + var call = ActiveCalls[callIndex]; + if (call != null) + { + EndCall(call); + } + else + { + Debug.LogMessage(LogEventLevel.Information, this, "[End Call] Unable to find call at index '{0}'", i); + } + }); + } + + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + CallStatusChange += (sender, args) => + { + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + Debug.LogMessage(LogEventLevel.Debug, this, "Call Direction: {0}", args.CallItem.Direction); + Debug.LogMessage(LogEventLevel.Debug, this, "Call is incoming: {0}", args.CallItem.Direction == eCodecCallDirection.Incoming); + trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status == eCodecCallStatus.Ringing); + + if (args.CallItem.Direction == eCodecCallDirection.Incoming) + { + trilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem)); + trilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem)); + trilist.SetString(joinMap.IncomingCallName.JoinNumber, args.CallItem.Name); + trilist.SetString(joinMap.IncomingCallNumber.JoinNumber, args.CallItem.Number); } else { - InitialSyncComplete = false; + trilist.SetString(joinMap.IncomingCallName.JoinNumber, string.Empty); + trilist.SetString(joinMap.IncomingCallNumber.JoinNumber, string.Empty); + } + + + trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); + + trilist.SetUshort(joinMap.ConnectedCallCount.JoinNumber, (ushort)ActiveCalls.Count); + }; + + if (this is IJoinCalls joinCodec) + { + trilist.SetSigFalseAction(joinMap.JoinAllCalls.JoinNumber, () => joinCodec.JoinAllCalls()); + + for (int i = 0; i < joinMap.JoinCallStart.JoinSpan; i++) + { + trilist.SetSigFalseAction((uint)(joinMap.JoinCallStart.JoinNumber + i), () => + { + var call = ActiveCalls[i]; + if (call != null) + { + joinCodec.JoinCall(call); + } + else + { + Debug.LogMessage(LogEventLevel.Information, this, "[Join Call] Unable to find call at index '{0}'", i); + } + }); + } + } + + if (this is IHasCallHold holdCodec) + { + trilist.SetSigFalseAction(joinMap.HoldAllCalls.JoinNumber, () => + { + foreach (var call in ActiveCalls) + { + holdCodec.HoldCall(call); + } + }); + + for (int i = 0; i < joinMap.HoldCallsStart.JoinSpan; i++) + { + var index = i; + + trilist.SetSigFalseAction((uint)(joinMap.HoldCallsStart.JoinNumber + index), () => + { + if (index < 0 || index >= ActiveCalls.Count) return; + + var call = ActiveCalls[index]; + if (call != null) + { + holdCodec.HoldCall(call); + } + else + { + Debug.LogMessage(LogEventLevel.Information, this, "[Hold Call] Unable to find call at index '{0}'", i); + } + }); + + trilist.SetSigFalseAction((uint)(joinMap.ResumeCallsStart.JoinNumber + index), () => + { + if (index < 0 || index >= ActiveCalls.Count) return; + + var call = ActiveCalls[index]; + if (call != null) + { + holdCodec.ResumeCall(call); + } + else + { + Debug.LogMessage(LogEventLevel.Information, this, "[Resume Call] Unable to find call at index '{0}'", i); + } + }); + } + } + + + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + // TODO [ ] #983 + Debug.LogMessage(LogEventLevel.Information, this, "LinkVideoCodecCallControlsToApi: device is {0}, IsInCall {1}", args.DeviceOnLine ? "online" : "offline", IsInCall); + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + trilist.SetString(joinMap.CurrentCallData.JoinNumber, "\xFC"); + trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); + }; + } + + private string UpdateCallStatusXSig() + { + const int maxCalls = 8; + const int maxStrings = 6; + const int maxDigitals = 2; + const int offset = maxStrings + maxDigitals; + var stringIndex = 0; + var digitalIndex = maxStrings * maxCalls; + var arrayIndex = 0; + + var tokenArray = new XSigToken[maxCalls * offset]; //set array size for number of calls * pieces of info + + foreach (var call in ActiveCalls) + { + if (arrayIndex >= maxCalls * offset) + break; + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, call.IsOnHold); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, call.Name ?? String.Empty); + tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, call.Number ?? String.Empty); + tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, call.Direction.ToString()); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, call.Type.ToString()); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, call.Status.ToString()); + if (call.Duration != null) + { + // May need to verify correct string format here + var dur = string.Format("{0:c}", call.Duration); + tokenArray[arrayIndex + 6] = new XSigSerialToken(stringIndex + 6, dur); + } + + arrayIndex += offset; + stringIndex += maxStrings; + digitalIndex += maxDigitals; + } + while (arrayIndex < maxCalls * offset) + { + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); + + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); + tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty); + tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty); + tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty); + + arrayIndex += offset; + stringIndex += maxStrings; + digitalIndex += maxDigitals; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecDtmfToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmfAction("0", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmfAction("1", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmfAction("2", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmfAction("3", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmfAction("4", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmfAction("5", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmfAction("6", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmfAction("7", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmfAction("8", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmfAction("9", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmfAction("*", trilist, joinMap)); + trilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmfAction("#", trilist, joinMap)); + } + + /// + /// Sends the specified string as a DTMF command. + /// Reads the value of the SendDtmfToSpecificCallInstance digital join and SelectCall analog join to determine + /// Whther to send to a specific call index or to the last connected call + /// + /// + /// + /// + private void SendDtmfAction(string s, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + if (!trilist.GetBool(joinMap.SendDtmfToSpecificCallIndex.JoinNumber)) + { + SendDtmf(s); + } + else + { + var callIndex = trilist.GetUshort(joinMap.SelectCall.JoinNumber); + if (callIndex > 0 && callIndex <= 8) + { + var call = ActiveCalls[callIndex - 1]; + if (call != null && call.IsActiveCall) + { + SendDtmf(s, call); + } + else + { + Debug.LogMessage(LogEventLevel.Information, this, "Warning: No call found at index {0} or call is not active.", callIndex); + } + } + else + { + Debug.LogMessage(LogEventLevel.Information, this, "Warning: Invalid call index specified. Please use a value of 1-8."); } } } + + private void LinkVideoCodecCameraLayoutsToApi(IHasCodecLayouts codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraLayout.JoinNumber, codec.LocalLayoutToggle); + + codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentLayoutStringFb.JoinNumber]); + } + + private void LinkVideoCodecCameraModeToApi(IHasCameraAutoMode codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeOn); + trilist.SetSigFalseAction(joinMap.CameraModeManual.JoinNumber, codec.CameraAutoModeOff); + + codec.CameraAutoModeIsOnFeedback.OutputChange += (o, a) => + { + if (codec is IHasCameraOff offCodec) + { + if (offCodec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + }; + + + if (codec is IHasCameraOff offModeCodec) + { + if (offModeCodec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + } + + private void LinkVideoCodecSelfviewToApi(IHasCodecSelfView codec, BasicTriList trilist, + VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraSelfView.JoinNumber, codec.SelfViewModeToggle); + + codec.SelfviewIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.CameraSelfView.JoinNumber]); + } + + private void LinkVideoCodecCameraToApi(IHasCodecCameras codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + //Camera PTZ + trilist.SetBoolSigAction(joinMap.CameraTiltUp.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + + if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; + + if (b) camera.TiltUp(); + else camera.TiltStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraTiltDown.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + + if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; + + if (b) camera.TiltDown(); + else camera.TiltStop(); + }); + trilist.SetBoolSigAction(joinMap.CameraPanLeft.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + + if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; + + if (b) camera.PanLeft(); + else camera.PanStop(); + }); + trilist.SetBoolSigAction(joinMap.CameraPanRight.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + + if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; + + if (b) camera.PanRight(); + else camera.PanStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraZoomIn.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + + if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; + + if (b) camera.ZoomIn(); + else camera.ZoomStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraZoomOut.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + + if (!(codec.SelectedCamera is IHasCameraPtzControl camera)) return; + + if (b) camera.ZoomOut(); + else camera.ZoomStop(); + }); + + + trilist.SetBoolSigAction(joinMap.CameraFocusNear.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + + if (!(codec.SelectedCamera is IHasCameraFocusControl camera)) return; + + if (b) camera.FocusNear(); + else camera.FocusStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraFocusFar.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + + if (!(codec.SelectedCamera is IHasCameraFocusControl camera)) return; + + if (b) camera.FocusFar(); + else camera.FocusStop(); + }); + + trilist.SetSigFalseAction(joinMap.CameraFocusAuto.JoinNumber, () => + { + if (codec.SelectedCamera == null) return; + + if (!(codec.SelectedCamera is IHasCameraFocusControl camera)) return; + + camera.TriggerAutoFocus(); + }); + + // Camera count + trilist.SetUshort(joinMap.CameraCount.JoinNumber, (ushort)codec.Cameras.Count); + + // Camera names + for (uint i = 0; i < joinMap.CameraNamesFb.JoinSpan; i++) + { + //Check the count first + if (i < codec.Cameras.Count && codec.Cameras[(int)i] != null) + { + trilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, codec.Cameras[(int)i].Name); + } + else + { + trilist.SetString(joinMap.CameraNamesFb.JoinNumber + i, ""); + } + } + + //Camera Select + trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) => + { + if (i > 0 && i <= codec.Cameras.Count) + { + codec.SelectCamera(codec.Cameras[i - 1].Key); + } + else + { + Debug.LogMessage(LogEventLevel.Information, this, "Unable to select. No camera found at index {0}", i); + } + }); + + // Set initial selected camera feedback + if (codec.SelectedCamera != null) + { + trilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)codec.Cameras.FindIndex((c) => c.Key == codec.SelectedCamera.Key)); + } + + codec.CameraSelected += (sender, args) => + { + var i = (ushort)codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key); + + trilist.SetUshort(joinMap.CameraNumberSelect.JoinNumber, (ushort)(i + 1)); + + if (codec is IHasCodecRoomPresets) + { + return; + } + + if (!(args.SelectedCamera is IHasCameraPresets)) + { + return; + } + + var cam = args.SelectedCamera as IHasCameraPresets; + SetCameraPresetNames(cam.Presets); + + (args.SelectedCamera as IHasCameraPresets).PresetsListHasChanged += (o, eventArgs) => SetCameraPresetNames(cam.Presets); + + trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, + (a) => + { + cam.PresetSelect(a); + trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, a); + }); + + trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, + () => + { + cam.PresetStore(trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, + String.Empty); + trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); + }); + }; + + if (!(codec is IHasCodecRoomPresets)) return; + + var presetCodec = codec as IHasCodecRoomPresets; + + presetCodec.CodecRoomPresetsListHasChanged += + (sender, args) => SetCameraPresetNames(presetCodec.NearEndPresets); + + //Camera Presets + trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, (i) => + { + presetCodec.CodecRoomPresetSelect(i); + }); + + + // Far End Presets + trilist.SetUShortSigAction(joinMap.FarEndPresetSelect.JoinNumber, (i) => + { + presetCodec.SelectFarEndPreset(i); + }); + + + trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, + () => + { + presetCodec.CodecRoomPresetStore( + trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, String.Empty); + trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); + }); + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + // TODO [ ] Issue #868 + trilist.SetString(joinMap.CameraPresetNames.JoinNumber, "\xFC"); + SetCameraPresetNames(presetCodec.NearEndPresets); + }; + } + + // Following fields only used for Bridging + private int _selectedRecentCallItemIndex; + private DirectoryItem _selectedDirectoryItem; + + private void LinkVideoCodecCallHistoryToApi(IHasCallHistory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.CallHistory.RecentCallsListHasChanged += (o, a) => + { + UpdateCallHistory(codec, trilist, joinMap); + }; + + // Selected item action and feedback + trilist.SetUShortSigAction(joinMap.SelectRecentCallItem.JoinNumber, (u) => + { + if (u == 0 || u > codec.CallHistory.RecentCalls.Count) + { + Debug.LogMessage(LogEventLevel.Verbose, this, "Recent Call History index out of range"); + return; + } + + _selectedRecentCallItemIndex = (int)(u - 1); + trilist.SetUshort(joinMap.SelectRecentCallItem.JoinNumber, u); + + var _selectedRecentCallItem = codec.CallHistory.RecentCalls[_selectedRecentCallItemIndex]; + + if (_selectedRecentCallItem != null) + { + trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, _selectedRecentCallItem.Name); + trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, _selectedRecentCallItem.Number); + trilist.SetSigFalseAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber, () => codec.RemoveCallHistoryEntry(_selectedRecentCallItem)); + trilist.SetSigFalseAction(joinMap.DialSelectedRecentCallItem.JoinNumber, () => this.Dial(_selectedRecentCallItem.Number)); + } + else + { + trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, string.Empty); + trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, string.Empty); + trilist.ClearBoolSigAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber); + trilist.ClearBoolSigAction(joinMap.DialSelectedRecentCallItem.JoinNumber); + } + }); + } + + + + private void UpdateCallHistory(IHasCallHistory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + // Clear out selected item + _selectedRecentCallItemIndex = 0; + + trilist.SetUshort(joinMap.SelectRecentCallItem.JoinNumber, 0); + trilist.SetString(joinMap.SelectedRecentCallName.JoinNumber, string.Empty); + trilist.SetString(joinMap.SelectedRecentCallNumber.JoinNumber, string.Empty); + trilist.ClearBoolSigAction(joinMap.RemoveSelectedRecentCallItem.JoinNumber); + // + + trilist.SetUshort(joinMap.RecentCallCount.JoinNumber, (ushort)codec.CallHistory.RecentCalls.Count); + + // Update the call history joins + var maxItems = joinMap.RecentCallNamesStart.JoinSpan; + + // Create history + uint index = 0; + for (uint i = 0; i < maxItems && i < codec.CallHistory.RecentCalls.Count; i++) + { + trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + i, codec.CallHistory.RecentCalls[(int)i].Name); + trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + i, codec.CallHistory.RecentCalls[(int)i].StartTime.ToShortTimeString()); + trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + i, (ushort)codec.CallHistory.RecentCalls[(int)i].OccurrenceType); + //i++; + index = i; + } + + //foreach(var item in codec.CallHistory.RecentCalls) + //{ + // trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + i, item.Name); + // trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + i, item.StartTime.ToShortTimeString()); + // trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + i, (ushort)item.OccurrenceType); + // i++; + //} + + // Clears existing items + for (uint j = index; j < maxItems; j++) + { + trilist.SetString(joinMap.RecentCallNamesStart.JoinNumber + j, string.Empty); + trilist.SetString(joinMap.RecentCallTimesStart.JoinNumber + j, string.Empty); + trilist.SetUshort(joinMap.RecentCallOccurrenceType.JoinNumber + j, 0); + } + } + + private string SetCameraPresetNames(IEnumerable presets) + { + return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); + } + + private string SetCameraPresetNames(IEnumerable presets) + { + return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); + } + + private string SetCameraPresetNames(ICollection presets) + { + var i = 1; //start index for xsig; + + var tokenArray = new XSigToken[presets.Count]; + + foreach (var preset in presets) + { + var cameraPreset = new XSigSerialToken(i, preset); + tokenArray[i - 1] = cameraPreset; + i++; + } + + return GetXSigString(tokenArray); + } + + private string GetXSigString(XSigToken[] tokenArray) + { + string returnString; + using (var s = new MemoryStream()) + { + using (var tw = new XSigTokenStreamWriter(s, true)) + { + tw.WriteXSigData(tokenArray); + } + + var xSig = s.ToArray(); + + returnString = Encoding.GetEncoding(XSigEncoding).GetString(xSig, 0, xSig.Length); + } + + return returnString; + } + + #endregion +} + + +/// +/// Used to track the status of syncronizing the phonebook values when connecting to a codec or refreshing the phonebook info +/// +public class CodecPhonebookSyncState : IKeyed +{ + private bool _InitialSyncComplete; + + public CodecPhonebookSyncState(string key) + { + Key = key; + + CodecDisconnected(); + } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + InitialSyncCompleted?.Invoke(this, new EventArgs()); + } + _InitialSyncComplete = value; + } + } + + public bool InitialPhonebookFoldersWasReceived { get; private set; } + + public bool NumberOfContactsWasReceived { get; private set; } + + public bool PhonebookRootEntriesWasRecieved { get; private set; } + + public bool PhonebookHasFolders { get; private set; } + + public int NumberOfContacts { get; private set; } + + #region IKeyed Members + + public string Key { get; private set; } + + #endregion + + public event EventHandler InitialSyncCompleted; + + public void InitialPhonebookFoldersReceived() + { + InitialPhonebookFoldersWasReceived = true; + + CheckSyncStatus(); + } + + public void PhonebookRootEntriesReceived() + { + PhonebookRootEntriesWasRecieved = true; + + CheckSyncStatus(); + } + + public void SetPhonebookHasFolders(bool value) + { + PhonebookHasFolders = value; + + Debug.LogMessage(LogEventLevel.Debug, this, "Phonebook has folders: {0}", PhonebookHasFolders); + } + + public void SetNumberOfContacts(int contacts) + { + NumberOfContacts = contacts; + NumberOfContactsWasReceived = true; + + Debug.LogMessage(LogEventLevel.Debug, this, "Phonebook contains {0} contacts.", NumberOfContacts); + + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + InitialPhonebookFoldersWasReceived = false; + PhonebookHasFolders = false; + NumberOfContacts = 0; + NumberOfContactsWasReceived = false; + } + + private void CheckSyncStatus() + { + if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved) + { + InitialSyncComplete = true; + Debug.LogMessage(LogEventLevel.Debug, this, "Initial Phonebook Sync Complete!"); + } + else + { + InitialSyncComplete = false; + } + } +} /// /// Represents a codec command that might need to have a friendly label applied for UI feedback purposes /// public class CodecCommandWithLabel { - public string Command { get; private set; } - public string Label { get; private set; } + public string Command { get; private set; } + public string Label { get; private set; } - public CodecCommandWithLabel(string command, string label) - { - Command = command; - Label = label; - } + public CodecCommandWithLabel(string command, string label) + { + Command = command; + Label = label; + } } diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index cb026a53..4eaeefaf 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using System.Threading.Tasks; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.Net.Http; using Crestron.SimplSharp.WebScripting; using Newtonsoft.Json; @@ -1829,7 +1829,7 @@ public class MobileControlSystemController : EssentialsDevice, IMobileControl /// /// /// - private void HandleError(object sender, ErrorEventArgs e) + private void HandleError(object sender, WebSocketSharp.ErrorEventArgs e) { this.LogError("Websocket error {0}", e.Message); diff --git a/src/PepperDash.Essentials/ControlSystem.cs b/src/PepperDash.Essentials/ControlSystem.cs index b42b7aad..3c6fba1a 100644 --- a/src/PepperDash.Essentials/ControlSystem.cs +++ b/src/PepperDash.Essentials/ControlSystem.cs @@ -1,6 +1,9 @@ -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; +using System; +using System.IO; +using System.Linq; using System.Reflection; +using System.Threading; +using Crestron.SimplSharp; using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.CrestronThread; using Crestron.SimplSharpPro.Diagnostics; @@ -9,19 +12,16 @@ using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using PepperDash.Essentials.Core.Web; -using System; -using System.Linq; -using Serilog.Events; using PepperDash.Essentials.Core.Routing; -using System.Threading; +using PepperDash.Essentials.Core.Web; +using Serilog.Events; using Timeout = Crestron.SimplSharp.Timeout; namespace PepperDash.Essentials; -/// -/// Represents the main control system for the application, providing initialization, configuration loading, and device -/// management functionality. +/// +/// Represents the main control system for the application, providing initialization, configuration loading, and device +/// management functionality. /// /// This class extends and serves as the entry point for the control /// system. It manages the initialization of devices, rooms, tie lines, and other system components. Additionally, it @@ -34,9 +34,9 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig private ManualResetEventSlim _initializeEvent; private const long StartupTime = 500; - /// - /// Initializes a new instance of the class, setting up the system's global state and - /// dependencies. + /// + /// Initializes a new instance of the class, setting up the system's global state and + /// dependencies. /// /// This constructor configures the control system by initializing key components such as the /// device manager and secrets manager, and sets global properties like the maximum number of user threads and the @@ -45,26 +45,26 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig public ControlSystem() : base() - { - try - { - Crestron.SimplSharpPro.CrestronThread.Thread.MaxNumberOfUserThreads = 400; - - Global.ControlSystem = this; - DeviceManager.Initialize(this); - SecretsManager.Initialize(); - SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true; - - Debug.SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? LogEventLevel.Warning : LogEventLevel.Verbose); + { + try + { + Crestron.SimplSharpPro.CrestronThread.Thread.MaxNumberOfUserThreads = 400; + + Global.ControlSystem = this; + DeviceManager.Initialize(this); + SecretsManager.Initialize(); + SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true; + + Debug.SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? LogEventLevel.Warning : LogEventLevel.Verbose); } catch (Exception e) { - Debug.LogError(e, "FATAL INITIALIZE ERROR. System is in an inconsistent state"); + Debug.LogError(e, "FATAL INITIALIZE ERROR. System is in an inconsistent state"); } } - /// - /// Initializes the control system and prepares it for operation. + /// + /// Initializes the control system and prepares it for operation. /// /// This method ensures that all devices in the system are properly registered and initialized /// before the system is fully operational. If the control system is of a DMPS type, the method waits for all @@ -78,7 +78,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig if (preventInitializationComplete) { Debug.LogMessage(LogEventLevel.Debug, "******************* Initializing System **********************"); - + _startTimer = new Timer(StartSystem, preventInitializationComplete, StartupTime, Timeout.Infinite); _initializeEvent = new ManualResetEventSlim(false); @@ -91,7 +91,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig _initializeEvent.Wait(30000); Debug.LogMessage(LogEventLevel.Debug, "******************* System Initialization Complete **********************"); - + SystemMonitor.ProgramInitialization.ProgramInitializationComplete = true; } else @@ -101,7 +101,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig } private void StartSystem(object preventInitialization) - { + { DeterminePlatform(); // Print .NET runtime version @@ -154,7 +154,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig CrestronConsole.AddNewConsoleCommand(DeviceManager.GetRoutingPorts, "getroutingports", "Reports all routing ports, if any. Requires a device key", ConsoleAccessLevelEnum.AccessOperator); - + if (!Debug.DoNotLoadConfigOnNextBoot) { @@ -183,16 +183,16 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig var dirSeparator = Global.DirectorySeparator; - string directoryPrefix; + string directoryPrefix; - directoryPrefix = Directory.GetApplicationRootDirectory(); + directoryPrefix = Directory.GetCurrentDirectory(); Global.SetAssemblyVersion(PluginLoader.GetAssemblyVersion(Assembly.GetExecutingAssembly())); if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) // Handles 3-series running Windows CE OS { string userFolder = "user"; - string nvramFolder = "nvram"; + string nvramFolder = "nvram"; Debug.LogMessage(LogEventLevel.Information, "Starting Essentials v{version:l} on {processorSeries:l} Appliance", Global.AssemblyVersion, "4-series"); //Debug.LogMessage(LogEventLevel.Information, "Starting Essentials v{0} on {1} Appliance", Global.AssemblyVersion, is4series ? "4-series" : "3-series"); @@ -201,8 +201,8 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig if (Directory.Exists(Global.ApplicationDirectoryPathPrefix + dirSeparator + userFolder + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber))) { - - Debug.LogMessage(LogEventLevel.Information, "{userFolder:l}/program{applicationNumber} directory found", userFolder, InitialParametersClass.ApplicationNumber); + + Debug.LogMessage(LogEventLevel.Information, "{userFolder:l}/program{applicationNumber} directory found", userFolder, InitialParametersClass.ApplicationNumber); filePathPrefix = directoryPrefix + dirSeparator + userFolder + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator; } @@ -211,7 +211,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber))) { Debug.LogMessage(LogEventLevel.Information, "{nvramFolder:l}/program{applicationNumber} directory found", nvramFolder, InitialParametersClass.ApplicationNumber); - + filePathPrefix = directoryPrefix + dirSeparator + nvramFolder + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator; } @@ -219,7 +219,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig else { Debug.LogMessage(LogEventLevel.Information, "{userFolder:l}/program{applicationNumber} directory found", userFolder, InitialParametersClass.ApplicationNumber); - + filePathPrefix = directoryPrefix + dirSeparator + userFolder + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator; } @@ -227,7 +227,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig else // Handles Linux OS (Virtual Control) { //Debug.SetDebugLevel(2); - Debug.LogMessage(LogEventLevel.Information, "Starting Essentials v{version:l} on Virtual Control Server", Global.AssemblyVersion); + Debug.LogMessage(LogEventLevel.Information, "Starting Essentials v{version:l} on Virtual Control Server", Global.AssemblyVersion); // Set path to User/ filePathPrefix = directoryPrefix + dirSeparator + "User" + dirSeparator; @@ -237,7 +237,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig } catch (Exception e) { - Debug.LogMessage(e, "Unable to determine platform due to exception"); + Debug.LogMessage(e, "Unable to determine platform due to exception"); } } @@ -252,7 +252,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig PluginLoader.AddProgramAssemblies(); - _ = new Core.DeviceFactory(); + _ = new Core.DeviceFactory(); Debug.LogMessage(LogEventLevel.Information, "Starting Essentials load from configuration"); @@ -299,7 +299,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig } - + /// /// Verifies filesystem is set up. IR, SGD, and programX folders @@ -312,54 +312,54 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig Debug.LogMessage(LogEventLevel.Information, "FilePathPrefix: {filePathPrefix:l}", configDir); var configExists = Directory.Exists(configDir); if (!configExists) - Directory.Create(configDir); + Directory.CreateDirectory(configDir); var irDir = Global.FilePathPrefix + "ir"; if (!Directory.Exists(irDir)) - Directory.Create(irDir); + Directory.CreateDirectory(irDir); var sgdDir = Global.FilePathPrefix + "sgd"; - if (!Directory.Exists(sgdDir)) - Directory.Create(sgdDir); + if (!Directory.Exists(sgdDir)) + Directory.CreateDirectory(sgdDir); var pluginDir = Global.FilePathPrefix + "plugins"; if (!Directory.Exists(pluginDir)) - Directory.Create(pluginDir); + Directory.CreateDirectory(pluginDir); var joinmapDir = Global.FilePathPrefix + "joinmaps"; - if(!Directory.Exists(joinmapDir)) - Directory.Create(joinmapDir); + if (!Directory.Exists(joinmapDir)) + Directory.CreateDirectory(joinmapDir); - return configExists; - } + return configExists; + } - /// - /// - /// - public void TearDown() - { - Debug.LogMessage(LogEventLevel.Information, "Tearing down existing system"); - DeviceManager.DeactivateAll(); + /// + /// + /// + public void TearDown() + { + Debug.LogMessage(LogEventLevel.Information, "Tearing down existing system"); + DeviceManager.DeactivateAll(); - TieLineCollection.Default.Clear(); + TieLineCollection.Default.Clear(); - foreach (var key in DeviceManager.GetDevices()) - DeviceManager.RemoveDevice(key); + foreach (var key in DeviceManager.GetDevices()) + DeviceManager.RemoveDevice(key); - Debug.LogMessage(LogEventLevel.Information, "Tear down COMPLETE"); - } + Debug.LogMessage(LogEventLevel.Information, "Tear down COMPLETE"); + } - /// - /// - /// + /// + /// + /// - void Load() - { - LoadDevices(); - LoadRooms(); - LoadLogoServer(); + void Load() + { + LoadDevices(); + LoadRooms(); + LoadLogoServer(); - DeviceManager.ActivateAll(); + DeviceManager.ActivateAll(); LoadTieLines(); @@ -368,8 +368,8 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig if (mobileControl == null) return; mobileControl.LinkSystemMonitorToAppServer();*/ - - } + + } /// /// Reads all devices from config and adds them to DeviceManager @@ -417,14 +417,14 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig if (newDev == null) newDev = Core.DeviceFactory.GetDevice(devConf); - if (newDev != null) - DeviceManager.AddDevice(newDev); - else + if (newDev != null) + DeviceManager.AddDevice(newDev); + else Debug.LogMessage(LogEventLevel.Information, "ERROR: Cannot load unknown device type '{deviceType:l}', key '{deviceKey:l}'.", devConf.Type, devConf.Key); } catch (Exception e) { - Debug.LogMessage(e, "ERROR: Creating device {deviceKey:l}. Skipping device.",args: new[] { devConf.Key }); + Debug.LogMessage(e, "ERROR: Creating device {deviceKey:l}. Skipping device.", args: new[] { devConf.Key }); } } Debug.LogMessage(LogEventLevel.Information, "All Devices Loaded."); @@ -462,27 +462,28 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig /// Reads all rooms from config and adds them to DeviceManager /// public void LoadRooms() - { + { if (ConfigReader.ConfigObject.Rooms == null) { Debug.LogMessage(LogEventLevel.Information, "Notice: Configuration contains no rooms - Is this intentional? This may be a valid configuration."); return; } - foreach (var roomConfig in ConfigReader.ConfigObject.Rooms) + foreach (var roomConfig in ConfigReader.ConfigObject.Rooms) { try { var room = Core.DeviceFactory.GetDevice(roomConfig); - if(room == null) + if (room == null) { Debug.LogWarning("ERROR: Cannot load unknown room type '{roomType:l}', key '{roomKey:l}'.", roomConfig.Type, roomConfig.Key); continue; } DeviceManager.AddDevice(room); - } catch (Exception ex) + } + catch (Exception ex) { Debug.LogMessage(ex, "Exception loading room {roomKey}:{roomType}", null, roomConfig.Key, roomConfig.Type); continue; @@ -551,7 +552,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig } catch { - Debug.LogMessage(LogEventLevel.Information, "Unable to find logo information in any room config"); + Debug.LogMessage(LogEventLevel.Information, "Unable to find logo information in any room config"); return false; } } diff --git a/src/PepperDash.Essentials/Factory/DeviceFactory.cs b/src/PepperDash.Essentials/Factory/DeviceFactory.cs index 425394f8..40575d1c 100644 --- a/src/PepperDash.Essentials/Factory/DeviceFactory.cs +++ b/src/PepperDash.Essentials/Factory/DeviceFactory.cs @@ -2,12 +2,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Crestron.SimplSharpPro; using System.Reflection; - +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; @@ -40,7 +39,7 @@ public class DeviceFactory } catch (Exception e) { - Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "Unable to load type: '{exception}' DeviceFactory: {factoryName}", e, type.Name); + Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "Unable to load type: '{exception}' DeviceFactory: {factoryName}", e, type.Name); } } } diff --git a/src/PepperDash.Essentials/HttpLogoServer.cs b/src/PepperDash.Essentials/HttpLogoServer.cs index 174ebd7e..7b807266 100644 --- a/src/PepperDash.Essentials/HttpLogoServer.cs +++ b/src/PepperDash.Essentials/HttpLogoServer.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.Net.Http; - using PepperDash.Core; using Serilog.Events; @@ -34,21 +33,21 @@ public class HttpLogoServer public HttpLogoServer(int port, string directory) { ExtensionContentTypes = new Dictionary - { + { //{ ".css", "text/css" }, //{ ".htm", "text/html" }, //{ ".html", "text/html" }, { ".jpg", "image/jpeg" }, - { ".jpeg", "image/jpeg" }, + { ".jpeg", "image/jpeg" }, //{ ".js", "application/javascript" }, //{ ".json", "application/json" }, //{ ".map", "application/x-navimap" }, { ".pdf", "application/pdf" }, - { ".png", "image/png" }, + { ".png", "image/png" }, //{ ".txt", "text/plain" }, }; - _server = new HttpServer {Port = port}; + _server = new HttpServer { Port = port }; _fileDirectory = directory; _server.OnHttpRequest += Server_OnHttpRequest; _server.Open(); @@ -62,7 +61,7 @@ public class HttpLogoServer void Server_OnHttpRequest(object sender, OnHttpRequestArgs args) { var path = args.Request.Path; - Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "HTTP Request with path: '{requestPath:l}'", args.Request.Path); + Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "HTTP Request with path: '{requestPath:l}'", args.Request.Path); try { @@ -75,7 +74,9 @@ public class HttpLogoServer if (File.Exists(localPath)) { args.Response.Header.ContentType = GetContentType(new FileInfo(localPath).Extension); - args.Response.ContentStream = new FileStream(localPath, FileMode.Open, FileAccess.Read); + // For .NET 8 migration, read file content as bytes since ContentStream expects Crestron.SimplSharp.CrestronIO.Stream + var fileBytes = File.ReadAllBytes(localPath); + args.Response.ContentBytes = fileBytes; } else {