diff --git a/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.projectinfo b/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.projectinfo index 6b5fb15e..33a04665 100644 Binary files a/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.projectinfo and b/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.projectinfo differ diff --git a/Essentials DM/Essentials_DM/Essentials_DM.projectinfo b/Essentials DM/Essentials_DM/Essentials_DM.projectinfo index 483e1974..dbf3a423 100644 Binary files a/Essentials DM/Essentials_DM/Essentials_DM.projectinfo and b/Essentials DM/Essentials_DM/Essentials_DM.projectinfo differ diff --git a/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.projectinfo b/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.projectinfo index 56e6cf1a..8a29be86 100644 Binary files a/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.projectinfo and b/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.projectinfo differ diff --git a/Essentials/PepperDashEssentials/Config/ConfigReader.cs b/Essentials/PepperDashEssentials/Config/ConfigReader.cs index 5d5fac98..abf788f6 100644 --- a/Essentials/PepperDashEssentials/Config/ConfigReader.cs +++ b/Essentials/PepperDashEssentials/Config/ConfigReader.cs @@ -1,138 +1,150 @@ -using System; -using System.Linq; -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using PepperDash.Core; -using PepperDash.Essentials.Core.Config; - -namespace PepperDash.Essentials -{ - /// - /// Loads the ConfigObject from the file - /// - public class ConfigReader - { - public static EssentialsConfig ConfigObject { get; private set; } - - public static void LoadConfig2() - { - Debug.Console(0, "Using unmerged system/template configs."); - try - { - using (StreamReader fs = new StreamReader(string.Format(@"\NVRAM\program{0}\ConfigurationFile.json", InitialParametersClass.ApplicationNumber))) - { - var doubleObj = JObject.Parse(fs.ReadToEnd()); - ConfigObject = MergeConfigs(doubleObj).ToObject(); - } - } - catch (Exception e) - { - Debug.Console(0, "Config failed: \r{0}", e); - } - - } - - 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"])); - else - merged.Add("info", template["info"]); - - merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray, system["devices"] as JArray, "uid")); - - if (system["rooms"] == null) - merged.Add("rooms", template["rooms"]); - else - merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray, system["rooms"] as JArray, "key")); - - if (system["sourceLists"] == null) - merged.Add("sourceLists", template["sourceLists"]); - else - merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"])); - -#warning Make tie lines merge appropriately - 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()); - - Debug.Console(0, "MERGED 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. - /// - static JArray MergeArraysOnTopLevelProperty(JArray a1, JArray a2, string propertyName) - { - var result = new JArray(); - if (a2 == null) - result = a1; - else if (a1 != null) - { - 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);// Merge(JObject.FromObject(a1Dev), JObject.FromObject(a2Match)); - result.Add(mergedItem); - } - else - result.Add(a1Dev); - } - } - return result; - } - - - /// - /// Helper for using with JTokens. Converts to JObject - /// - static JObject Merge(JToken t1, JToken t2) - { - return Merge(JObject.FromObject(t1), JObject.FromObject(t2)); - } - - /// - /// Merge b ONTO a - /// - /// - /// - static JObject Merge(JObject o1, JObject o2) - { - //Console.WriteLine("Merging {0}\ronto {1}", o2, o1); - foreach (var o2Prop in o2) - { - var o1Value = o1[o2Prop.Key]; - if (o1Value == null) - o1.Add(o2Prop.Key, o2Prop.Value); - else - { - JToken replacement = null; - if (o2Prop.Value.HasValues && o1Value.HasValues) // Drill down - replacement = Merge(JObject.FromObject(o1Value), JObject.FromObject(o2Prop.Value)); - else - replacement = o2Prop.Value; - o1[o2Prop.Key].Replace(replacement); - } - } - return o1; - } - - } +using System; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials +{ + /// + /// Loads the ConfigObject from the file + /// + public class ConfigReader + { + public static EssentialsConfig ConfigObject { get; private set; } + + public static void LoadConfig2() + { + Debug.Console(0, "Using unmerged system/template configs."); + try + { + using (StreamReader fs = new StreamReader(string.Format(@"\NVRAM\program{0}\ConfigurationFile.json", InitialParametersClass.ApplicationNumber))) + { + var doubleObj = JObject.Parse(fs.ReadToEnd()); + ConfigObject = MergeConfigs(doubleObj).ToObject(); + + // Extract SystemUrl and TemplateUrl + + if (doubleObj["system_url"] != null) + { + ConfigObject.SystemUrl = doubleObj["system_url"].Value(); + } + + if (doubleObj["template_url"] != null) + { + ConfigObject.TemplateUrl= doubleObj["template_url"].Value(); + } + } + } + catch (Exception e) + { + Debug.Console(0, "Config failed: \r{0}", e); + } + + } + + 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"])); + else + merged.Add("info", template["info"]); + + merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray, system["devices"] as JArray, "uid")); + + if (system["rooms"] == null) + merged.Add("rooms", template["rooms"]); + else + merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray, system["rooms"] as JArray, "key")); + + if (system["sourceLists"] == null) + merged.Add("sourceLists", template["sourceLists"]); + else + merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"])); + +#warning Make tie lines merge appropriately + 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()); + + Debug.Console(0, "MERGED 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. + /// + static JArray MergeArraysOnTopLevelProperty(JArray a1, JArray a2, string propertyName) + { + var result = new JArray(); + if (a2 == null) + result = a1; + else if (a1 != null) + { + 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);// Merge(JObject.FromObject(a1Dev), JObject.FromObject(a2Match)); + result.Add(mergedItem); + } + else + result.Add(a1Dev); + } + } + return result; + } + + + /// + /// Helper for using with JTokens. Converts to JObject + /// + static JObject Merge(JToken t1, JToken t2) + { + return Merge(JObject.FromObject(t1), JObject.FromObject(t2)); + } + + /// + /// Merge b ONTO a + /// + /// + /// + static JObject Merge(JObject o1, JObject o2) + { + //Console.WriteLine("Merging {0}\ronto {1}", o2, o1); + foreach (var o2Prop in o2) + { + var o1Value = o1[o2Prop.Key]; + if (o1Value == null) + o1.Add(o2Prop.Key, o2Prop.Value); + else + { + JToken replacement = null; + if (o2Prop.Value.HasValues && o1Value.HasValues) // Drill down + replacement = Merge(JObject.FromObject(o1Value), JObject.FromObject(o2Prop.Value)); + else + replacement = o2Prop.Value; + o1[o2Prop.Key].Replace(replacement); + } + } + return o1; + } + + } } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Config/EssentialsConfig.cs b/Essentials/PepperDashEssentials/Config/EssentialsConfig.cs index de83739c..e158c810 100644 --- a/Essentials/PepperDashEssentials/Config/EssentialsConfig.cs +++ b/Essentials/PepperDashEssentials/Config/EssentialsConfig.cs @@ -1,29 +1,57 @@ -using System; -using System.Collections.Generic; -using Crestron.SimplSharp.CrestronIO; - -using Newtonsoft.Json; -using PepperDash.Core; -using PepperDash.Essentials.Core.Config; - -namespace PepperDash.Essentials -{ - /// - /// Loads the ConfigObject from the file - /// - public class EssentialsConfig : BasicConfig - { - [JsonProperty("rooms")] - public List Rooms { get; private set; } - } - - /// - /// - /// - public class SystemTemplateConfigs - { - public EssentialsConfig System { get; set; } - - public EssentialsConfig Template { get; set; } - } +using System; +using System.Collections.Generic; +using Crestron.SimplSharp.CrestronIO; + +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; +using System.Text.RegularExpressions; + +namespace PepperDash.Essentials +{ + /// + /// Loads the ConfigObject from the file + /// + public class EssentialsConfig : BasicConfig + { + public string SystemUrl { get; set; } + public string TemplateUrl { get; set; } + + public string SystemUuid + { + get + { + var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/templates\/(.*)#.*"); + + string uuid = result.Groups[1].Value; + + return uuid; + } + } + + public string TemplateUuid + { + get + { + var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)#.*"); + + string uuid = result.Groups[1].Value; + + return uuid; + } + } + + [JsonProperty("rooms")] + public List Rooms { get; private set; } + } + + /// + /// + /// + public class SystemTemplateConfigs + { + public EssentialsConfig System { get; set; } + + public EssentialsConfig Template { get; set; } + } } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs b/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs index 3deeccb9..03f41cb3 100644 --- a/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs +++ b/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs @@ -321,7 +321,7 @@ namespace PepperDash.Essentials.Fusion "\n" + "\n"; - Debug.Console(1, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); + Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; } @@ -336,21 +336,12 @@ namespace PepperDash.Essentials.Fusion { DateTime now = DateTime.Today; - //string currentTime = string.Format("Current time: {0:D4}-{1:D2}-{2:D2}T{3:D2}:{4:D2}:{5:D2}", now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); - string currentTime = now.ToString("s"); - Debug.Console(1, this, "Current time: {0}", currentTime); - - //Debug.Console(1, this, "Current time: {0}", now.ToString("d")); - - //string requestTest = - // string.Format("{0}{1}2017-05-02T00:00:0024", requestID, GUID); - string requestTest = string.Format("FullSchedleRequest{0}{1}24", RoomGuid, currentTime); - Debug.Console(1, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest); + Debug.Console(2, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest); FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest; @@ -465,7 +456,7 @@ namespace PepperDash.Essentials.Fusion "" + ""; - Debug.Console(1, this, "Sending CreateMeeting Request: \n{0}", createMeetingRequest); + Debug.Console(2, this, "Sending CreateMeeting Request: \n{0}", createMeetingRequest); FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = createMeetingRequest; @@ -482,7 +473,7 @@ namespace PepperDash.Essentials.Fusion /// void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) { - Debug.Console(1, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); + Debug.Console(2, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQueryResponse) @@ -578,7 +569,7 @@ namespace PepperDash.Essentials.Fusion /// void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) { - Debug.Console(1, this, "Scehdule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); + Debug.Console(2, this, "Scehdule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse) @@ -674,7 +665,7 @@ namespace PepperDash.Essentials.Fusion } else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse) { - Debug.Console(1, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); + Debug.Console(2, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); } } diff --git a/Essentials/PepperDashEssentials/PepperDashEssentials.projectinfo b/Essentials/PepperDashEssentials/PepperDashEssentials.projectinfo index 8f657ecb..e2cb3d80 100644 Binary files a/Essentials/PepperDashEssentials/PepperDashEssentials.projectinfo and b/Essentials/PepperDashEssentials/PepperDashEssentials.projectinfo differ diff --git a/Essentials/PepperDashEssentials/UI/CotijaInterfaceController.cs b/Essentials/PepperDashEssentials/UI/CotijaInterfaceController.cs index 7aa3752b..8f8bfc9c 100644 --- a/Essentials/PepperDashEssentials/UI/CotijaInterfaceController.cs +++ b/Essentials/PepperDashEssentials/UI/CotijaInterfaceController.cs @@ -14,77 +14,134 @@ namespace PepperDash.Essentials { public class CotijaInterfaceController : Device { - StreamReader Reader; + GenericHttpSseClient SseClient; - Crestron.SimplSharp.Net.Connection Connection; + CCriticalSection FileLock; + + string ServerUrl; public CotijaInterfaceController(string key) : base(key) { - CrestronConsole.AddNewConsoleCommand(InitializeClientAndEventStream, "InitializeHttpClient", "Initializes a new HTTP client connection to a specified URL", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(RegisterRoomToServer, "InitializeHttpClient", "Initializes a new HTTP client connection to a specified URL", ConsoleAccessLevelEnum.AccessOperator); } - public void InitializeClientAndEventStream(string url) + /// + /// Registers the room with the server + /// + /// URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port" + void RegisterRoomToServer(string url) { try { - HttpClient webClient = new HttpClient(); - webClient.Verbose = true; - HttpClientRequest request = new HttpClientRequest(); - request.Url = new UrlParser(url); - request.Header.AddHeader(new HttpHeader("Accept", "text/event-stream")); - request.KeepAlive = true; + ServerUrl = url; - /// Experimenting with grabbing connection before the request - Connection = webClient.ConnectNew("192.168.1.120", 3000); - Debug.Console(1, this, "Connection Port: {0}", Connection.LocalEndPointPort); - Reader = new StreamReader(Connection); - Connection.OnBytesReceived += new EventHandler(DataConnection_OnBytesReceived); + string filePath = string.Format(@"\NVRAM\Program{0}\configurationFile.json", Global.ControlSystem.ProgramNumber); + string postBody = null; - /// Not sure if this is starting a new connection - webClient.DispatchAsync(request, StreamConnectionCallback); + if (string.IsNullOrEmpty(filePath)) + { + Debug.Console(0, this, "Error reading file. No path specified."); + return; + } + + FileLock = new CCriticalSection(); + + if (FileLock.TryEnter()) + { + Debug.Console(1, this, "Reading Configuration File"); + + postBody = File.ReadToEnd(filePath, Encoding.ASCII); + + Debug.Console(2, this, "{0}", postBody); + + FileLock.Leave(); + } + + if (string.IsNullOrEmpty(postBody)) + { + Debug.Console(1, "Post Body is null or empty"); + } + else + { + HttpClient Client = new HttpClient(); + + HttpClientRequest Request = new HttpClientRequest(); + + Client.Verbose = true; + Client.KeepAlive = true; + + string uuid = Essentials.ConfigReader.ConfigObject.SystemUuid; + + url = string.Format("http://{0}/api/system/join/{1}", ServerUrl, uuid); + + Request.Url.Parse(url); + Request.RequestType = RequestType.Post; + Request.Header.SetHeaderValue("Content-Type", "application/json"); + Request.ContentString = postBody; + + Client.DispatchAsync(Request, PostConnectionCallback); + } } catch (Exception e) { - Debug.Console(1, this, "Error Initializing Cotija client:\n{0}", e); + Debug.Console(0, this, "Error Initilizing Room: {0}", e); + } + + } + + /// + /// The callback that fires when we get a response from our registration attempt + /// + /// + /// + void PostConnectionCallback(HttpClientResponse resp, HTTP_CALLBACK_ERROR err) + { + try + { + if (resp.Code == 200) + { + Debug.Console(0, this, "Initializing SSE Client."); + + if (SseClient == null) + { + SseClient = new GenericHttpSseClient(string.Format("{0}-SseClient", Key), Name); + } + else + { + if (SseClient.IsConnected) + { + SseClient.Disconnect(); + } + + string uuid = Essentials.ConfigReader.ConfigObject.SystemUuid; + + SseClient.Url = string.Format("http://{0}/api/system/stream/{1}", ServerUrl, uuid); + + SseClient.Connect(); + } + + CommunicationGather LineGathered = new CommunicationGather(SseClient, "\x0d\x0a"); + + LineGathered.LineReceived += new EventHandler(LineGathered_LineReceived); + + + } + else + { + Debug.Console(0, this, "Unable to initialize SSE Client"); + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error Initializeing SSE Client: {0}", e); } } - // This callback happens but none of the events would fire (when not commented out) - void StreamConnectionCallback(HttpClientResponse resp, HTTP_CALLBACK_ERROR err) + void LineGathered_LineReceived(object sender, GenericCommMethodReceiveTextArgs e) { - Debug.Console(1, this, "Connection Port: {0}", resp.DataConnection.LocalEndPointPort); - Debug.Console(1, this, "Connections are equal {0}", resp.DataConnection == Connection); - Debug.Console(1, this, "Callback Fired"); - //resp.DataConnection.OnBytesReceived += new EventHandler(DataConnection_OnBytesReceived); - //Debug.Console(1, this, "Received: '{0}'", resp.ContentString); - //resp.OnTransferEnd += new Crestron.SimplSharp.Net.TransferEndEventHandler(resp_OnTransferEnd); - //resp.OnTransferProgress += new Crestron.SimplSharp.Net.TransferProgressEventHandler(resp_OnTransferProgress); - //resp.OnTransferStart += new Crestron.SimplSharp.Net.TransferStartEventHandler(resp_OnTransferStart); + Debug.Console(1, this, "Received from Node Server: '{0}'", e.Text); } - // We could never get this event handler to fire - void DataConnection_OnBytesReceived(object sender, EventArgs e) - { - Debug.Console(1, this, "OnBytesReceived Event Fired."); - Debug.Console(1, this, "Event: Received: '{0}'", Reader.ReadToEnd()); - } - - //void resp_OnTransferStart(object sender, Crestron.SimplSharp.Net.TransferStartEventArgs e) - //{ - // Debug.Console(1, this, "OnTransferStart"); - //} - - //void resp_OnTransferProgress(object sender, Crestron.SimplSharp.Net.TransferProgressEventArgs e) - //{ - // Debug.Console(1, this, "OnTransferProgress"); - //} - - - - //void resp_OnTransferEnd(object sender, Crestron.SimplSharp.Net.TransferEndEventArgs e) - //{ - // Debug.Console(1, this, "OnTransferEnd"); - //} } } \ No newline at end of file