diff --git a/Pepperdash Core/Pepperdash Core.suo b/Pepperdash Core/Pepperdash Core.suo new file mode 100644 index 0000000..88da250 Binary files /dev/null and b/Pepperdash Core/Pepperdash Core.suo differ diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs index 8b30fe6..be4dc92 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs @@ -29,6 +29,31 @@ namespace PepperDash.Core //public event GenericSocketStatusChangeEventDelegate SocketStatusChange; public event EventHandler ConnectionChange; + /// + /// Address of server + /// + public string Hostname { get; set; } + + /// + /// Port on server + /// + public int Port { get; set; } + + /// + /// Another damn S+ helper because S+ seems to treat large port nums as signed ints + /// which screws up things + /// + public ushort UPort + { + get { return Convert.ToUInt16(Port); } + set { Port = Convert.ToInt32(value); } + } + + /// + /// Defaults to 2000 + /// + public int BufferSize { get; set; } + /// /// /// @@ -37,33 +62,70 @@ namespace PepperDash.Core /// /// /// - public bool IsConnected { get { return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; } } + public bool IsConnected + { + get { return Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; } + } + + /// + /// S+ helper for IsConnected + /// + public ushort UIsConnected + { + get { return (ushort)(IsConnected ? 1 : 0); } + } /// /// /// - public SocketStatus ClientStatus { get { return Client.ClientStatus; } } - - /// - /// - /// - public string ClientStatusText { get { return Client.ClientStatus.ToString(); } } + public SocketStatus ClientStatus + { + get + { + if (Client == null) + return SocketStatus.SOCKET_STATUS_NO_CONNECT; + return Client.ClientStatus; + } + } + + /// + /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event + /// and IsConnected with be true when this == 2. + /// + public ushort UStatus + { + get { return (ushort)ClientStatus; } + } /// /// /// - public ushort UClientStatus { get { return (ushort)Client.ClientStatus; } } + public string ClientStatusText { get { return ClientStatus.ToString(); } } + + [Obsolete] + /// + /// + /// + public ushort UClientStatus { get { return (ushort)ClientStatus; } } /// /// /// - public string ConnectionFailure { get { return Client.ClientStatus.ToString(); } } + public string ConnectionFailure { get { return ClientStatus.ToString(); } } /// /// /// public bool AutoReconnect { get; set; } - + + /// + /// S+ helper for AutoReconnect + /// + public ushort UAutoReconnect + { + get { return (ushort)(AutoReconnect ? 1 : 0); } + set { AutoReconnect = value == 1; } + } /// /// Milliseconds to wait before attempting to reconnect. Defaults to 5000 /// @@ -87,24 +149,60 @@ namespace PepperDash.Core public GenericTcpIpClient(string key, string address, int port, int bufferSize) : base(key) { - if (string.IsNullOrEmpty(address)) - { - Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", key); - return; - } - if (port < 1 || port > 65535) - { - { - Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': Invalid port", key); - return; - } - } + Hostname = address; + Port = port; + BufferSize = bufferSize; AutoReconnectIntervalMs = 5000; - Client = new TCPClient(address, port, bufferSize); - Client.SocketStatusChange += Client_SocketStatusChange; + //if (string.IsNullOrEmpty(address)) + //{ + // Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", key); + // return; + //} + //if (port < 1 || port > 65535) + //{ + // { + // Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': Invalid port", key); + // return; + // } + //} + + //Client = new TCPClient(address, port, bufferSize); + //Client.SocketStatusChange += Client_SocketStatusChange; } + public GenericTcpIpClient() + : base("Uninitialized TcpIpClient") + { + CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); + AutoReconnectIntervalMs = 5000; + BufferSize = 2000; + } + + /// + /// Just to help S+ set the key + /// + public void Initialize(string key) + { + Key = key; + } + + /// + /// Handles closing this up when the program shuts down + /// + void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType == eProgramStatusEventType.Stopping) + { + if (Client != null) + { + Debug.Console(1, this, "Program stopping. Closing connection"); + Client.DisconnectFromServer(); + Client.Dispose(); + } + } + } + //public override bool CustomActivate() //{ // return true; @@ -112,12 +210,32 @@ namespace PepperDash.Core public override bool Deactivate() { - Client.SocketStatusChange -= this.Client_SocketStatusChange; + if(Client != null) + Client.SocketStatusChange -= this.Client_SocketStatusChange; return true; } public void Connect() { + if (IsConnected) + return; + + if (string.IsNullOrEmpty(Hostname)) + { + Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", Key); + return; + } + if (Port < 1 || Port > 65535) + { + { + Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': Invalid port", Key); + return; + } + } + + Client = new TCPClient(Hostname, Port, BufferSize); + Client.SocketStatusChange += Client_SocketStatusChange; + Client.ConnectToServerAsync(null); DisconnectCalledByUser = false; } diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/EventArgs and Constants.cs b/Pepperdash Core/Pepperdash Core/JsonToSimpl/EventArgs and Constants.cs new file mode 100644 index 0000000..42ecf35 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/JsonToSimpl/EventArgs and Constants.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Core.JsonToSimpl +{ + public class JsonToSimplConstants + { + public const ushort JsonIsValidBoolChange = 2; + + public const ushort BoolValueChange = 1; + public const ushort UshortValueChange = 101; + public const ushort StringValueChange = 201; + } + + //**************************************************************************************************// + public delegate void SPlusValuesDelegate(); + + public class SPlusValueWrapper + { + public SPlusType ValueType { get; private set; } + public ushort Index { get; private set; } + public ushort BoolUShortValue { get; set; } + public string StringValue { get; set; } + + public SPlusValueWrapper() { } + + public SPlusValueWrapper(SPlusType type, ushort index) + { + ValueType = type; + Index = index; + } + } + + public enum SPlusType + { + Digital, Analog, String + } + + + //**************************************************************************************************// + public class BoolChangeEventArgs : EventArgs + { + public bool State { get; set; } + public ushort IntValue { get { return (ushort)(State ? 1 : 0); } } + public ushort Type { get; set; } + public ushort Index { get; set; } + + public BoolChangeEventArgs() + { + } + + public BoolChangeEventArgs(bool state, ushort type) + { + State = state; + Type = type; + } + } + + //**************************************************************************************************// + public class UshrtChangeEventArgs : EventArgs + { + public ushort IntValue { get; set; } + public ushort Type { get; set; } + public ushort Index { get; set; } + + public UshrtChangeEventArgs() + { + } + + public UshrtChangeEventArgs(ushort intValue, ushort type) + { + IntValue = intValue; + Type = type; + } + } + + //**************************************************************************************************// + public class StringChangeEventArgs : EventArgs + { + public string StringValue { get; set; } + public ushort Type { get; set; } + public ushort Index { get; set; } + + public StringChangeEventArgs() + { + } + + public StringChangeEventArgs(string stringValue, ushort type) + { + StringValue = stringValue; + Type = type; + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/Global.cs b/Pepperdash Core/Pepperdash Core/JsonToSimpl/Global.cs new file mode 100644 index 0000000..4621c15 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/JsonToSimpl/Global.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +//using PepperDash.Core; + +namespace PepperDash.Core.JsonToSimpl +{ + public class J2SGlobal + { + static List Masters = new List(); + + + /// + /// Adds a file master. If the master's key or filename is equivalent to any existing + /// master, this will fail + /// + /// New master to add + public static void AddMaster(JsonToSimplMaster master) + { + if (master == null) + throw new ArgumentNullException("master"); + + if (string.IsNullOrEmpty(master.UniqueID)) + throw new InvalidOperationException("JSON Master cannot be added with a null UniqueId"); + + Debug.Console(0, "JSON Global adding master {0}", master.UniqueID); + + if (Masters.Contains(master)) return; + + var existing = Masters.FirstOrDefault(m => + m.UniqueID.Equals(master.UniqueID, StringComparison.OrdinalIgnoreCase)); + if (existing == null) + { + Masters.Add(master); + } + else + { + var msg = string.Format("Cannot add JSON Master with unique ID '{0}'.\rID is already in use on another master.", master.UniqueID); + CrestronConsole.PrintLine(msg); + ErrorLog.Warn(msg); + } + } + + /// + /// Gets a master by its key. Case-insensitive + /// + public static JsonToSimplMaster GetMasterByFile(string file) + { + return Masters.FirstOrDefault(m => m.UniqueID.Equals(file, StringComparison.OrdinalIgnoreCase)); + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs new file mode 100644 index 0000000..c95bf2f --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Core.JsonToSimpl +{ + public class JsonToSimplArrayLookupChild : JsonToSimplChildObjectBase + { + public string SearchPropertyName { get; set; } + public string SearchPropertyValue { get; set; } + + int ArrayIndex; + + public void Initialize(string file, string key, string pathPrefix, string pathSuffix, + string searchPropertyName, string searchPropertyValue) + { + base.Initialize(file, key, pathPrefix, pathSuffix); + SearchPropertyName = searchPropertyName; + SearchPropertyValue = searchPropertyValue; + } + + + //PathPrefix+ArrayName+[x]+path+PathSuffix + /// + /// + /// + /// + /// + protected override string GetFullPath(string path) + { + return string.Format("{0}[{1}].{2}{3}", + PathPrefix == null ? "" : PathPrefix, + ArrayIndex, path, + PathSuffix == null ? "" : PathSuffix); + } + + public override void ProcessAll() + { + if(FindInArray()) + base.ProcessAll(); + } + + bool FindInArray() + { + if (Master == null) + throw new InvalidOperationException("Cannot do operations before master is linked"); + if (Master.JsonObject == null) + throw new InvalidOperationException("Cannot do operations before master JSON has read"); + if (PathPrefix == null) + throw new InvalidOperationException("Cannot do operations before PathPrefix is set"); + + var token = Master.JsonObject.SelectToken(PathPrefix); + if (token is JArray) + { + var array = token as JArray; + try + { + var item = array.FirstOrDefault(o => + { + var prop = o[SearchPropertyName]; + return prop != null && prop.Value() + .Equals(SearchPropertyValue, StringComparison.OrdinalIgnoreCase); + }); + if (item == null) + { + Debug.Console(0, "Child[{0}] Array '{1}' '{2}={3}' not found: ", Key, + PathPrefix, SearchPropertyName, SearchPropertyValue); + return false; + } + + ArrayIndex = array.IndexOf(item); + Debug.Console(0, "Child[{0}] Found array match at index {1}", Key, ArrayIndex); + return true; + } + catch (Exception e) + { + Debug.Console(0, "Child[{0}] Array '{1}' lookup error: '{2}={3}'\r{4}", Key, + PathPrefix, SearchPropertyName, SearchPropertyValue, e); + } + } + else + Debug.Console(0, "Child[{0}] Path '{1}' is not an array", Key, PathPrefix); + + return false; + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplChildObjectBase.cs b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplChildObjectBase.cs new file mode 100644 index 0000000..d3720de --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplChildObjectBase.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Core.JsonToSimpl +{ + public abstract class JsonToSimplChildObjectBase: IKeyed + { + + public event EventHandler BoolChange; + public event EventHandler UShortChange; + public event EventHandler StringChange; + + public SPlusValuesDelegate GetAllValuesDelegate { get; set; } + + /// + /// Use a callback to reduce task switch/threading + /// + public SPlusValuesDelegate SetAllPathsDelegate { get; set; } + + public string Key { get; protected set; } + + /// + /// This will be prepended to all paths to allow path swapping or for more organized + /// sub-paths + /// + public string PathPrefix { get; protected set; } + + /// + /// This is added to the end of all paths + /// + public string PathSuffix { get; protected set; } + + protected JsonToSimplMaster Master; + + // The sent-in JPaths for the various types + protected Dictionary BoolPaths = new Dictionary(); + protected Dictionary UshortPaths = new Dictionary(); + protected Dictionary StringPaths = new Dictionary(); + + /// + /// Call this before doing anything else + /// + /// + /// + /// + /// + public void Initialize(string masterUniqueId, string key, string pathPrefix, string pathSuffix) + { + Key = key; + PathPrefix = pathPrefix; + PathSuffix = pathSuffix; + + Master = J2SGlobal.GetMasterByFile(masterUniqueId); + if (Master != null) + Master.AddChild(this); + else + Debug.Console(0, "JSON Child [{0}] cannot link to master {1}", key, masterUniqueId); + } + + /// + /// Set the JPath to evaluate for a given bool out index. + /// + public void SetBoolPath(ushort index, string path) + { + Debug.Console(0, "JSON Child[{0}] SetBoolPath {1}={2}", Key, index, path); + if (path == null || path.Trim() == string.Empty) return; + BoolPaths[index] = path; + } + + /// + /// Set the JPath for a ushort out index. + /// + public void SetUshortPath(ushort index, string path) + { + Debug.Console(0, "JSON Child[{0}] SetUshortPath {1}={2}", Key, index, path); + if (path == null || path.Trim() == string.Empty) return; + UshortPaths[index] = path; + } + + /// + /// Set the JPath for a string output index. + /// + public void SetStringPath(ushort index, string path) + { + Debug.Console(0, "JSON Child[{0}] SetStringPath {1}={2}", Key, index, path); + if (path == null || path.Trim() == string.Empty) return; + StringPaths[index] = path; + } + + /// + /// Evalutates all outputs with defined paths. called by S+ when paths are ready to process + /// and by Master when file is read. + /// + public virtual void ProcessAll() + { + if (SetAllPathsDelegate == null) + { + Debug.Console(1, this, "No SetAllPathsDelegate set. Ignoring ProcessAll"); + return; + } + SetAllPathsDelegate(); + foreach (var kvp in BoolPaths) + ProcessBoolPath(kvp.Key); + foreach (var kvp in UshortPaths) + ProcessUshortPath(kvp.Key); + foreach (var kvp in StringPaths) + ProcessStringPath(kvp.Key); + } + + /// + /// Processes a bool property, converting to bool, firing off a BoolChange event + /// + void ProcessBoolPath(ushort index) + { + string response; + if (Process(BoolPaths[index], out response)) + OnBoolChange(response.Equals("true", StringComparison.OrdinalIgnoreCase), + index, JsonToSimplConstants.BoolValueChange); + else + OnBoolChange(false, index, JsonToSimplConstants.BoolValueChange); + } + + // Processes the path to a ushort, converting to ushort if able, firing off UshrtChange event + void ProcessUshortPath(ushort index) + { + string response; + if (Process(UshortPaths[index], out response)) + { + ushort val; + try { val = Convert.ToUInt16(response); } + catch { val = 0; } + OnUShortChange(val, index, JsonToSimplConstants.UshortValueChange); + } + else + OnUShortChange(0, index, JsonToSimplConstants.UshortValueChange); + } + + // Processes the path to a string property and fires of a StringChange event. + void ProcessStringPath(ushort index) + { + string response; + if (Process(StringPaths[index], out response)) + OnStringChange(response, index, JsonToSimplConstants.StringValueChange); + else + OnStringChange("", index, JsonToSimplConstants.StringValueChange); + } + + /// + /// Processes the given path. + /// + /// JPath formatted path to the desired property + /// The string value of the property, or a default value if it + /// doesn't exist + /// This will return false in the case that EvaulateAllOnJsonChange + /// is false and the path does not evaluate to a property in the incoming JSON. + bool Process(string path, out string response) + { + path = GetFullPath(path); + Debug.Console(0, "Child[{0}] Processing {1}", Key, path); + response = ""; + if (Master == null) + { + Debug.Console(0, "JSONChild[{0}] cannot process without Master attached", Key); + return false; + } + + if (Master.JsonObject != null && path != string.Empty) + { + bool isCount = false; + path = path.Trim(); + if (path.EndsWith(".Count")) + { + path = path.Remove(path.Length - 6, 6); + isCount = true; + } + try // Catch a strange cast error on a bad path + { + var t = Master.JsonObject.SelectToken(path); + if (t != null) + { + // return the count of children objects - if any + if (isCount) + response = (t.HasValues ? t.Children().Count() : 0).ToString(); + else + response = t.Value(); + Debug.Console(0, " ='{0}'", response); + return true; + } + } + catch + { + response = ""; + } + } + // If the path isn't found, return this to determine whether to pass out the non-value or not. + return false; + } + + + //************************************************************************************************ + // Save-related functions + + + /// + /// Called from Master to read inputs and update their values in master JObject + /// Callback should hit one of the following four methods + /// + public void UpdateInputsForMaster() + { + if (SetAllPathsDelegate == null) + { + Debug.Console(1, this, "No SetAllPathsDelegate set. Ignoring UpdateInputsForMaster"); + return; + } + SetAllPathsDelegate(); + var del = GetAllValuesDelegate; + if (del != null) + GetAllValuesDelegate(); + } + + public void USetBoolValue(ushort key, ushort theValue) + { + SetBoolValue(key, theValue == 1); + } + + public void SetBoolValue(ushort key, bool theValue) + { + if (BoolPaths.ContainsKey(key)) + SetValueOnMaster(BoolPaths[key], new JValue(theValue)); + } + + public void SetUShortValue(ushort key, ushort theValue) + { + if (UshortPaths.ContainsKey(key)) + SetValueOnMaster(UshortPaths[key], new JValue(theValue)); + } + + public void SetStringValue(ushort key, string theValue) + { + if (StringPaths.ContainsKey(key)) + SetValueOnMaster(StringPaths[key], new JValue(theValue)); + } + + public void SetValueOnMaster(string keyPath, JValue valueToSave) + { + var path = GetFullPath(keyPath); + try + { + Debug.Console(0, "Child[{0}] Queueing value on master {1}='{2}'", Key, path, valueToSave); + + //var token = Master.JsonObject.SelectToken(path); + //if (token != null) // The path exists in the file + Master.AddUnsavedValue(path, valueToSave); + } + catch (Exception e) + { + Debug.Console(0, "Child[{0}] Failed setting value for path '{1}'\r{2}", Key, path, e); + } + } + + /// + /// Called during Process(...) to get the path to a given property. By default, + /// returns PathPrefix+path+PathSuffix. Override to change the way path is built. + /// + protected virtual string GetFullPath(string path) + { + return (PathPrefix != null ? PathPrefix : "") + + path + (PathSuffix != null ? PathSuffix : ""); + } + + // Helpers for events + //****************************************************************************************** + protected void OnBoolChange(bool state, ushort index, ushort type) + { + var handler = BoolChange; + if (handler != null) + { + var args = new BoolChangeEventArgs(state, type); + args.Index = index; + BoolChange(this, args); + } + } + + //****************************************************************************************** + protected void OnUShortChange(ushort state, ushort index, ushort type) + { + var handler = UShortChange; + if (handler != null) + { + var args = new UshrtChangeEventArgs(state, type); + args.Index = index; + UShortChange(this, args); + } + } + + protected void OnStringChange(string value, ushort index, ushort type) + { + var handler = StringChange; + if (handler != null) + { + var args = new StringChangeEventArgs(value, type); + args.Index = index; + StringChange(this, args); + } + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs new file mode 100644 index 0000000..09d0201 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs @@ -0,0 +1,175 @@ +using System; +//using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Core.JsonToSimpl +{ + public class JsonToSimplFileMaster : JsonToSimplMaster + { + /// + /// Sets the filepath as well as registers this with the Global.Masters list + /// + public string Filepath { get; private set; } + + string ActualFilePath; + + /*****************************************************************************************/ + /** Privates **/ + + + // The JSON file in JObject form + // For gathering the incoming data + object StringBuilderLock = new object(); + // To prevent multiple same-file access + static object FileLock = new object(); + + /*****************************************************************************************/ + + /// + /// SIMPL+ default constructor. + /// + public JsonToSimplFileMaster() + { + } + + /// + /// Read, evaluate and udpate status + /// + public void EvaluateFile(string filepath) + { + Filepath = filepath; + + OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange); + if (string.IsNullOrEmpty(Filepath)) + { + CrestronConsole.PrintLine("Cannot evaluate file. JSON file path not set"); + return; + } + + // Resolve wildcard + var dir = Path.GetDirectoryName(Filepath); + Debug.Console(0, "Checking directory {0}", dir); + var fileName = Path.GetFileName(Filepath); + var directory = new DirectoryInfo(dir); + + var actualFile = directory.GetFiles(fileName).FirstOrDefault(); + if(actualFile == null) + { + var msg = string.Format("JSON file not found: {0}", Filepath); + CrestronConsole.PrintLine(msg); + ErrorLog.Error(msg); + return; + } + //var actualFileName = actualFile.FullName; + ActualFilePath = actualFile.FullName; + Debug.Console(0, "Actual JSON file is {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); + ErrorLog.Error(msg); + return; + } + } + + 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(0, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key); + child.UpdateInputsForMaster(); + } + + if (UnsavedValues == null || UnsavedValues.Count == 0) + { + Debug.Console(0, "Master [{0}] No updated values to save. Skipping", UniqueID); + return; + } + lock (FileLock) + { + Debug.Console(0, "Saving"); + foreach (var path in UnsavedValues.Keys) + { + var tokenToReplace = JsonObject.SelectToken(path); + if (tokenToReplace != null) + {// It's found + tokenToReplace.Replace(UnsavedValues[path]); + Debug.Console(0, "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(0, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path); + +// JContainer jpart = JsonObject; +// // walk down the path and find where it goes +//#warning Does not handle arrays. +// foreach (var part in path.Split('.')) +// { + +// var openPos = part.IndexOf('['); +// if (openPos > -1) +// { +// openPos++; // move to number +// var closePos = part.IndexOf(']'); +// var arrayName = part.Substring(0, openPos - 1); // get the name +// var index = Convert.ToInt32(part.Substring(openPos, closePos - openPos)); + +// // Check if the array itself exists and add the item if so +// if (jpart[arrayName] != null) +// { +// var arrayObj = jpart[arrayName] as JArray; +// var item = arrayObj[index]; +// if (item == null) +// arrayObj.Add(new JObject()); +// } + +// Debug.Console(0, "IGNORING MISSING ARRAY VALUE FOR NOW"); +// continue; +// } +// // Build the +// if (jpart[part] == null) +// jpart.Add(new JProperty(part, new JObject())); +// jpart = jpart[part] as JContainer; +// } +// jpart.Replace(UnsavedValues[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/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFixedPathObject.cs b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFixedPathObject.cs new file mode 100644 index 0000000..d674d03 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFixedPathObject.cs @@ -0,0 +1,20 @@ +using System; +//using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Core.JsonToSimpl +{ + + public class JsonToSimplFixedPathObject : JsonToSimplChildObjectBase + { + public JsonToSimplFixedPathObject() + { + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplGenericMaster.cs b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplGenericMaster.cs new file mode 100644 index 0000000..d83748f --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplGenericMaster.cs @@ -0,0 +1,153 @@ +using System; +//using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.Net.Http; +using Crestron.SimplSharp.Net.Https; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Core.JsonToSimpl +{ + public class JsonToSimplGenericMaster : JsonToSimplMaster + { + /*****************************************************************************************/ + /** Privates **/ + + + // The JSON file in JObject form + // For gathering the incoming data + object StringBuilderLock = new object(); + // To prevent multiple same-file access + static object WriteLock = new object(); + + public Action SaveCallback { get; set; } + + /*****************************************************************************************/ + + /// + /// SIMPL+ default constructor. + /// + public JsonToSimplGenericMaster() + { + } + + /// + /// Loads in JSON and triggers evaluation on all children + /// + /// + public void LoadWithJson(string json) + { + OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange); + 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); + ErrorLog.Error(msg); + } + } + + /// + /// Loads JSON into JsonObject, but does not trigger evaluation by children + /// + /// + public void SetJsonWithoutEvaluating(string json) + { + try + { + JsonObject = JObject.Parse(json); + } + catch (Exception e) + { + Debug.Console(0, this, "JSON parsing failed:\r{0}", e); + } + } + + /// + /// + /// + 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, this, "Master. checking child [{0}] for updates to save", child.Key); + child.UpdateInputsForMaster(); + } + + if (UnsavedValues == null || UnsavedValues.Count == 0) + { + Debug.Console(1, this, "Master. No updated values to save. Skipping"); + return; + } + + lock (WriteLock) + { + Debug.Console(1, this, "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, this, "Master Updating '{0}'", path); + } + else // No token. Let's make one + { + Debug.Console(1, "Master Cannot write value onto missing property: '{0}'", path); + } + } + } + if (SaveCallback != null) + SaveCallback(JsonObject.ToString()); + else + Debug.Console(0, this, "WARNING: No save callback defined."); + } + } +} + + +// JContainer jpart = JsonObject; +// // walk down the path and find where it goes +//#warning Does not handle arrays. +// foreach (var part in path.Split('.')) +// { + +// var openPos = part.IndexOf('['); +// if (openPos > -1) +// { +// openPos++; // move to number +// var closePos = part.IndexOf(']'); +// var arrayName = part.Substring(0, openPos - 1); // get the name +// var index = Convert.ToInt32(part.Substring(openPos, closePos - openPos)); + +// // Check if the array itself exists and add the item if so +// if (jpart[arrayName] != null) +// { +// var arrayObj = jpart[arrayName] as JArray; +// var item = arrayObj[index]; +// if (item == null) +// arrayObj.Add(new JObject()); +// } + +// Debug.Console(0, "IGNORING MISSING ARRAY VALUE FOR NOW"); +// continue; +// } +// // Build the +// if (jpart[part] == null) +// jpart.Add(new JProperty(part, new JObject())); +// jpart = jpart[part] as JContainer; +// } +// jpart.Replace(UnsavedValues[path]); \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplMaster.cs b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplMaster.cs new file mode 100644 index 0000000..531183f --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplMaster.cs @@ -0,0 +1,191 @@ +using System; +//using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace PepperDash.Core.JsonToSimpl +{ + public abstract class JsonToSimplMaster : IKeyed + { + /*****************************************************************************************/ + /** Events **/ + + public event EventHandler BoolChange; + public event EventHandler UshrtChange; + public event EventHandler StringChange; + + protected List Children = new List(); + + /*****************************************************************************************/ + + /// + /// Mirrors the Unique ID for now. + /// + public string Key { get { return UniqueID; } } + + public string UniqueID { get; protected set; } + + /// + /// Merely for use in debug messages + /// + public string DebugName + { + get { return _DebugName; } + set { if (DebugName == null) _DebugName = ""; else _DebugName = value; } + } + string _DebugName = ""; + + /// + /// This will be prepended to all paths to allow path swapping or for more organized + /// sub-paths + /// + public string PathPrefix { get; set; } + + /// + /// This is added to the end of all paths + /// + public string PathSuffix { get; set; } + + /// + /// Enables debugging output to the console. Certain error messages will be logged to the + /// system's error log regardless of this setting + /// + public bool DebugOn { get; set; } + + /// + /// Ushort helper for Debug property + /// + public ushort UDebug + { + get { return (ushort)(DebugOn ? 1 : 0); } + set + { + DebugOn = (value == 1); + CrestronConsole.PrintLine("JsonToSimpl debug={0}", DebugOn); + } + } + + public JObject JsonObject { get; protected set; } + + /*****************************************************************************************/ + /** Privates **/ + + + // The JSON file in JObject form + // For gathering the incoming data + protected Dictionary UnsavedValues = new Dictionary(); + + /*****************************************************************************************/ + + /// + /// SIMPL+ default constructor. + /// + public JsonToSimplMaster() + { + } + + + /// + /// Sets up class - overriding methods should always call this. + /// + /// + public virtual void Initialize(string uniqueId) + { + UniqueID = uniqueId; + J2SGlobal.AddMaster(this); // Should not re-add + } + + /// + /// Adds a child "module" to this master + /// + /// + public void AddChild(JsonToSimplChildObjectBase child) + { + if (Children.Contains(child)) return; + Children.Add(child); + } + + /// + /// Called from the child to add changed or new values for saving + /// + public void AddUnsavedValue(string path, JValue value) + { + if (UnsavedValues.ContainsKey(path)) + { + Debug.Console(0, "Master[{0}] WARNING - Attempt to add duplicate value for path '{1}'.\r Ingoring. Please ensure that path does not exist on multiple modules.", UniqueID, path); + } + else + UnsavedValues.Add(path, value); + //Debug.Console(0, "Master[{0}] Unsaved size={1}", UniqueID, UnsavedValues.Count); + } + + public abstract void Save(); + + + //****************************************************************************************** + public static class JsonFixes + { + public static JObject ParseObject(string json) + { + using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json))) + { + var startDepth = reader.Depth; + var obj = JObject.Load(reader); + if (startDepth != reader.Depth) + throw new JsonSerializationException("Unenclosed json found"); + return obj; + } + } + + public static JArray ParseArray(string json) + { + using (var reader = new JsonTextReader(new Crestron.SimplSharp.CrestronIO.StringReader(json))) + { + var startDepth = reader.Depth; + var obj = JArray.Load(reader); + if (startDepth != reader.Depth) + throw new JsonSerializationException("Unenclosed json found"); + return obj; + } + } + } + + // Helpers for events + //****************************************************************************************** + protected void OnBoolChange(bool state, ushort index, ushort type) + { + if (BoolChange != null) + { + var args = new BoolChangeEventArgs(state, type); + args.Index = index; + BoolChange(this, args); + } + } + + //****************************************************************************************** + protected void OnUshrtChange(ushort state, ushort index, ushort type) + { + if (UshrtChange != null) + { + var args = new UshrtChangeEventArgs(state, type); + args.Index = index; + UshrtChange(this, args); + } + } + + protected void OnStringChange(string value, ushort index, ushort type) + { + if (StringChange != null) + { + var args = new StringChangeEventArgs(value, type); + args.Index = index; + StringChange(this, args); + } + } + } +} diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/REMOVE JsonToSimplFixedPathObject.cs b/Pepperdash Core/Pepperdash Core/JsonToSimpl/REMOVE JsonToSimplFixedPathObject.cs new file mode 100644 index 0000000..ef41a77 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/JsonToSimpl/REMOVE JsonToSimplFixedPathObject.cs @@ -0,0 +1,20 @@ +//using System; +////using System.IO; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using Crestron.SimplSharp; +//using Crestron.SimplSharp.CrestronIO; +//using Newtonsoft.Json; +//using Newtonsoft.Json.Linq; + +//namespace PepperDash.Core.JsonToSimpl +//{ + +// public class JsonToSimplFixedPathObject : JsonToSimplChildObjectBase +// { +// public JsonToSimplFixedPathObject() +// { +// } +// } +//} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj index cbbd678..78b82da 100644 --- a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj +++ b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj @@ -73,8 +73,20 @@ + + + + + + + + + + + + diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.projectinfo b/Pepperdash Core/Pepperdash Core/PepperDash_Core.projectinfo index 05a209e..b6af2c8 100644 Binary files a/Pepperdash Core/Pepperdash Core/PepperDash_Core.projectinfo and b/Pepperdash Core/Pepperdash Core/PepperDash_Core.projectinfo differ diff --git a/Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs b/Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs index b343790..1d473e8 100644 --- a/Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs +++ b/Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs @@ -4,5 +4,5 @@ [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Pepperdash_Core")] [assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyVersion("1.0.0.*")] +[assembly: AssemblyVersion("1.0.*")] diff --git a/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2016-12-29 13-56-10).log b/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2016-12-29 13-56-10).log new file mode 100644 index 0000000..b986c42 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2016-12-29 13-56-10).log @@ -0,0 +1,11 @@ +12/29/2016 1:56:10 PM, Info: Initializing SIMPLSharp Services... +12/29/2016 1:56:10 PM, Info: ProjectInfo successfully initialized. +12/29/2016 2:14:42 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +12/29/2016 2:14:42 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +12/29/2016 2:14:42 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +12/29/2016 2:14:43 PM, Info: Saving project information... +12/29/2016 2:16:45 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +12/29/2016 2:16:46 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +12/29/2016 2:16:46 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +12/29/2016 2:16:46 PM, Info: Saving project information... +1/3/2017 2:45:48 PM, Info: Terminating SIMPLSharp Services diff --git a/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-03 15-30-31).log b/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-03 15-30-31).log new file mode 100644 index 0000000..c324e72 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-03 15-30-31).log @@ -0,0 +1,3 @@ +1/3/2017 3:30:31 PM, Info: Initializing SIMPLSharp Services... +1/3/2017 3:30:32 PM, Info: ProjectInfo successfully initialized. +1/3/2017 3:33:21 PM, Info: Terminating SIMPLSharp Services diff --git a/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-03 15-34-10).log b/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-03 15-34-10).log new file mode 100644 index 0000000..8049ac6 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-03 15-34-10).log @@ -0,0 +1,7 @@ +1/3/2017 3:34:10 PM, Info: Initializing SIMPLSharp Services... +1/3/2017 3:34:10 PM, Info: ProjectInfo successfully initialized. +1/3/2017 3:35:44 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/3/2017 3:35:44 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/3/2017 3:35:44 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/3/2017 3:35:45 PM, Info: Saving project information... +1/3/2017 4:32:09 PM, Info: Terminating SIMPLSharp Services diff --git a/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-04 09-38-26).log b/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-04 09-38-26).log new file mode 100644 index 0000000..fe6721e --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-04 09-38-26).log @@ -0,0 +1,29 @@ +1/4/2017 9:38:26 AM, Info: Initializing SIMPLSharp Services... +1/4/2017 9:38:27 AM, Info: ProjectInfo successfully initialized. +1/4/2017 9:48:06 AM, Info: Saving project information... +1/4/2017 9:48:06 AM, Info: Saving project information... +1/4/2017 9:50:00 AM, Info: Saving project information... +1/4/2017 9:50:00 AM, Info: Saving project information... +1/4/2017 9:50:00 AM, Info: Saving project information... +1/4/2017 9:50:00 AM, Info: Saving project information... +1/4/2017 9:50:01 AM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 9:50:02 AM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 9:50:02 AM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 9:50:02 AM, Info: Saving project information... +1/4/2017 10:37:54 AM, Info: Saving project information... +1/4/2017 10:37:54 AM, Info: Saving project information... +1/4/2017 10:37:54 AM, Info: Saving project information... +1/4/2017 10:37:54 AM, Info: Saving project information... +1/4/2017 10:37:54 AM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 10:37:55 AM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 10:37:55 AM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 10:37:55 AM, Info: Saving project information... +1/4/2017 10:44:01 AM, Info: Saving project information... +1/4/2017 10:44:01 AM, Info: Saving project information... +1/4/2017 10:44:01 AM, Info: Saving project information... +1/4/2017 10:44:01 AM, Info: Saving project information... +1/4/2017 10:44:02 AM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 10:44:02 AM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 10:44:02 AM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 10:44:03 AM, Info: Saving project information... +1/4/2017 11:06:47 AM, Info: Terminating SIMPLSharp Services diff --git a/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-04 12-35-17).log b/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-04 12-35-17).log new file mode 100644 index 0000000..628014d --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/SIMPLSharpLogs/(2017-01-04 12-35-17).log @@ -0,0 +1,39 @@ +1/4/2017 12:35:17 PM, Info: Initializing SIMPLSharp Services... +1/4/2017 12:35:17 PM, Info: ProjectInfo successfully initialized. +1/4/2017 12:43:03 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 12:43:04 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 12:43:04 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 12:43:04 PM, Info: Saving project information... +1/4/2017 1:50:06 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 1:50:06 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 1:50:06 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 1:50:06 PM, Info: Saving project information... +1/4/2017 1:55:36 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 1:55:36 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 1:55:36 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 1:55:36 PM, Info: Saving project information... +1/4/2017 1:58:48 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 1:58:48 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 1:58:48 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 1:58:48 PM, Info: Saving project information... +1/4/2017 2:00:00 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 2:00:01 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 2:00:01 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 2:00:01 PM, Info: Saving project information... +1/4/2017 2:00:10 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 2:00:11 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 2:00:11 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 2:00:11 PM, Info: Saving project information... +1/4/2017 2:00:32 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 2:00:32 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 2:00:32 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 2:00:33 PM, Info: Saving project information... +1/4/2017 2:18:17 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 2:18:18 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 2:18:18 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 2:18:18 PM, Info: Saving project information... +1/4/2017 2:31:43 PM, Info: Validating assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll... +1/4/2017 2:31:43 PM, Info: Verifying assembly C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +1/4/2017 2:31:43 PM, Info: Creating Archive C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.clz... +1/4/2017 2:31:44 PM, Info: Saving project information... +1/4/2017 2:41:56 PM, Info: Terminating SIMPLSharp Services diff --git a/Pepperdash Core/Pepperdash Core/WebApi/Presets/Preset.cs b/Pepperdash Core/Pepperdash Core/WebApi/Presets/Preset.cs new file mode 100644 index 0000000..bd93d61 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/WebApi/Presets/Preset.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; + + +namespace PepperDash.Core.WebApi.Presets +{ + public class Preset + { + public int Id { get; set; } + + public int UserId { get; set; } + + public int RoomTypeId { get; set; } + + public string PresetName { get; set; } + + public int PresetNumber { get; set; } + + public string Data { get; set; } + + public Preset() + { + PresetName = ""; + PresetNumber = 1; + Data = "{}"; + } + } + + /// + /// + /// + public class PresetReceivedEventArgs : EventArgs + { + public Preset Preset { get; private set; } + + /// + /// For Simpl+ + /// + public PresetReceivedEventArgs() { } + + public PresetReceivedEventArgs(Preset preset) + { + Preset = preset; + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/WebApi/Presets/User.cs b/Pepperdash Core/Pepperdash Core/WebApi/Presets/User.cs new file mode 100644 index 0000000..3551277 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/WebApi/Presets/User.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +namespace PepperDash.Core.WebApi.Presets +{ + /// + /// + /// + public class User + { + public int Id { get; set; } + + public string ExternalId { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } + } + + + /// + /// + /// + public class UserReceivedEventArgs : EventArgs + { + public User User { get; private set; } + + /// + /// For Simpl+ + /// + public UserReceivedEventArgs() { } + + public UserReceivedEventArgs(User user) + { + User = user; + } + } + + /// + /// + /// + public class UserAndRoomMessage + { + public int UserId { get; set; } + + public int RoomTypeId { get; set; } + + public int PresetNumber { get; set; } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/WebApi/Presets/WebApiPasscodeClient.cs b/Pepperdash Core/Pepperdash Core/WebApi/Presets/WebApiPasscodeClient.cs new file mode 100644 index 0000000..bb7e306 --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/WebApi/Presets/WebApiPasscodeClient.cs @@ -0,0 +1,231 @@ +using System; +using System.Text; +using Crestron.SimplSharp; // For Basic SIMPL# Classes +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.Net; +using Crestron.SimplSharp.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; +using PepperDash.Core.JsonToSimpl; + + +namespace PepperDash.Core.WebApi.Presets +{ + public class WebApiPasscodeClient : IKeyed + { + public event EventHandler UserReceived; + + public event EventHandler PresetReceived; + + public string Key { get; private set; } + + //string JsonMasterKey; + + /// + /// An embedded JsonToSimpl master object. + /// + JsonToSimplGenericMaster J2SMaster; + + string UrlBase; + + string DefaultPresetJsonFilePath; + + User CurrentUser; + + Preset CurrentPreset; + + + /// + /// SIMPL+ can only execute the default constructor. If you have variables that require initialization, please + /// use an Initialize method + /// + public WebApiPasscodeClient() + { + } + + public void Initialize(string key, string jsonMasterKey, string urlBase, string defaultPresetJsonFilePath) + { + Key = key; + //JsonMasterKey = jsonMasterKey; + UrlBase = urlBase; + DefaultPresetJsonFilePath = defaultPresetJsonFilePath; + + J2SMaster = new JsonToSimplGenericMaster(); + J2SMaster.SaveCallback = this.SaveCallback; + J2SMaster.Initialize(jsonMasterKey); + } + + public void GetUserForPasscode(string passcode) + { + + + var req = new HttpClientRequest(); + req.Url = new UrlParser(UrlBase + "/api/users/dopin"); + req.RequestType = RequestType.Post; + req.Header.AddHeader(new HttpHeader("Content-Type", "application/json")); + req.Header.AddHeader(new HttpHeader("Accept", "application/json")); + var jo = new JObject(); + jo.Add("pin", passcode); + req.ContentString = jo.ToString(); + + var client = new HttpClient(); + var resp = client.Dispatch(req); + if (resp.Code == 200) + { + CrestronConsole.PrintLine("Received: {0}", resp.ContentString); + var user = JsonConvert.DeserializeObject(resp.ContentString); +#warning CHECK for user success here?? + CurrentUser = user; + var handler = UserReceived; + if (handler != null) + UserReceived(this, new UserReceivedEventArgs(user)); + } + else + CrestronConsole.PrintLine("No user received: {0}", resp.Code); + } + + /// + /// + /// + /// + /// + public void GetPresetForThisUser(int roomTypeId, int presetNumber) + { + if (CurrentUser == null) + { + CrestronConsole.PrintLine("GetPresetForThisUser no user loaded"); + return; + } + + var msg = new UserAndRoomMessage + { + UserId = CurrentUser.Id, + RoomTypeId = roomTypeId, + PresetNumber = presetNumber + }; + + var req = new HttpClientRequest(); + req.Url = new UrlParser(UrlBase + "/api/presets/userandroom"); + req.RequestType = RequestType.Post; + req.Header.AddHeader(new HttpHeader("Content-Type", "application/json")); + req.Header.AddHeader(new HttpHeader("Accept", "application/json")); + req.ContentString = JsonConvert.SerializeObject(msg); + + var client = new HttpClient(); + try + { + var resp = client.Dispatch(req); + if (resp.Code == 200) + { + Debug.Console(1, this, "Received: {0}", resp.ContentString); + var preset = JsonConvert.DeserializeObject(resp.ContentString); + + CurrentPreset = preset; + + //if there's no preset data, load the template + if (preset.Data == null || preset.Data.Trim() == string.Empty || JObject.Parse(preset.Data).Count == 0) + { + Debug.Console(1, this, "Loaded preset has no data. Loading default template."); + LoadDefaultPresetData(); + return; + } + + J2SMaster.LoadWithJson(preset.Data); + var handler = PresetReceived; + if (handler != null) + PresetReceived(this, new PresetReceivedEventArgs(preset)); + } + } + catch (HttpException e) + { + var resp = e.Response; + Debug.Console(1, this, "No preset received (code {0}). Loading default template", resp.Code); + LoadDefaultPresetData(); + } + } + + void LoadDefaultPresetData() + { + CurrentPreset = null; + if (!File.Exists(DefaultPresetJsonFilePath)) + { + Debug.Console(0, this, "Cannot load default preset file. Saving will not work"); + return; + } + using (StreamReader sr = new StreamReader(DefaultPresetJsonFilePath)) + { + try + { + var data = sr.ReadToEnd(); + J2SMaster.SetJsonWithoutEvaluating(data); + CurrentPreset = new Preset() { Data = data, UserId = CurrentUser.Id }; + } + catch (Exception e) + { + Debug.Console(0, this, "Error reading default preset JSON: \r{0}", e); + } + } + } + + /// + /// + /// + /// + /// + public void SavePresetForThisUser(int roomTypeId, int presetNumber) + { + if (CurrentPreset == null) + LoadDefaultPresetData(); + //return; + + //// A new preset needs to have its numbers set + //if (CurrentPreset.IsNewPreset) + //{ + CurrentPreset.UserId = CurrentUser.Id; + CurrentPreset.RoomTypeId = roomTypeId; + CurrentPreset.PresetNumber = presetNumber; + //} + J2SMaster.Save(); // Will trigger callback when ready + } + + /// + /// After save operation on JSON master happens, send it to server + /// + /// + void SaveCallback(string json) + { + CurrentPreset.Data = json; + + var req = new HttpClientRequest(); + req.RequestType = RequestType.Post; + req.Url = new UrlParser(string.Format("{0}/api/presets/addorchange", UrlBase)); + req.Header.AddHeader(new HttpHeader("Content-Type", "application/json")); + req.Header.AddHeader(new HttpHeader("Accept", "application/json")); + req.ContentString = JsonConvert.SerializeObject(CurrentPreset); + + var client = new HttpClient(); + try + { + var resp = client.Dispatch(req); + + // 201=created + // 204=empty content + if (resp.Code == 201) + CrestronConsole.PrintLine("Preset added"); + else if (resp.Code == 204) + CrestronConsole.PrintLine("Preset updated"); + else if (resp.Code == 209) + CrestronConsole.PrintLine("Preset already exists. Cannot save as new."); + else + CrestronConsole.PrintLine("Preset save failed: {0}\r", resp.Code, resp.ContentString); + } + catch (HttpException e) + { + + CrestronConsole.PrintLine("Preset save exception {0}", e.Response.Code); + } + } + } +} diff --git a/Pepperdash Core/Pepperdash Core/bin/Newtonsoft.Json.Compact.dll b/Pepperdash Core/Pepperdash Core/bin/Newtonsoft.Json.Compact.dll new file mode 100644 index 0000000..e72180a Binary files /dev/null and b/Pepperdash Core/Pepperdash Core/bin/Newtonsoft.Json.Compact.dll differ diff --git a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.clz b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.clz index 6e97690..6d65179 100644 Binary files a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.clz and b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.clz differ diff --git a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.config b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.config index a8cdc38..f19580e 100644 --- a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.config +++ b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.config @@ -10,8 +10,8 @@ - 12/7/2016 11:49:31 AM - 1.0.0.21285 + 1/4/2017 2:31:43 PM + 1.0.6213.26151 Crestron.SIMPLSharp, Version=2.0.48.0, Culture=neutral, PublicKeyToken=812d080f93e2de10 diff --git a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.dll b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.dll index 1bc1c97..ba68cd1 100644 Binary files a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.dll and b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.dll differ diff --git a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.pdb b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.pdb new file mode 100644 index 0000000..2f65b59 Binary files /dev/null and b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.pdb differ diff --git a/Pepperdash Core/Pepperdash Core/bin/SimplSharpCustomAttributesInterface.dll b/Pepperdash Core/Pepperdash Core/bin/SimplSharpCustomAttributesInterface.dll new file mode 100644 index 0000000..6fd71ec Binary files /dev/null and b/Pepperdash Core/Pepperdash Core/bin/SimplSharpCustomAttributesInterface.dll differ diff --git a/Pepperdash Core/Pepperdash Core/bin/SimplSharpHelperInterface.dll b/Pepperdash Core/Pepperdash Core/bin/SimplSharpHelperInterface.dll new file mode 100644 index 0000000..78ba937 Binary files /dev/null and b/Pepperdash Core/Pepperdash Core/bin/SimplSharpHelperInterface.dll differ diff --git a/Pepperdash Core/Pepperdash Core/bin/SimplSharpNewtonsoft.dll b/Pepperdash Core/Pepperdash Core/bin/SimplSharpNewtonsoft.dll new file mode 100644 index 0000000..81103a7 Binary files /dev/null and b/Pepperdash Core/Pepperdash Core/bin/SimplSharpNewtonsoft.dll differ diff --git a/Pepperdash Core/Pepperdash Core/bin/SimplSharpReflectionInterface.dll b/Pepperdash Core/Pepperdash Core/bin/SimplSharpReflectionInterface.dll new file mode 100644 index 0000000..edc2f9f Binary files /dev/null and b/Pepperdash Core/Pepperdash Core/bin/SimplSharpReflectionInterface.dll differ diff --git a/Pepperdash Core/Pepperdash Core/bin/SimplSharpSQLHelperInterface.dll b/Pepperdash Core/Pepperdash Core/bin/SimplSharpSQLHelperInterface.dll new file mode 100644 index 0000000..69b6dc7 Binary files /dev/null and b/Pepperdash Core/Pepperdash Core/bin/SimplSharpSQLHelperInterface.dll differ diff --git a/Pepperdash Core/Pepperdash Core/bin/manifest.info b/Pepperdash Core/Pepperdash Core/bin/manifest.info index dafe65a..01d88af 100644 --- a/Pepperdash Core/Pepperdash Core/bin/manifest.info +++ b/Pepperdash Core/Pepperdash Core/bin/manifest.info @@ -1,7 +1,11 @@ -MainAssembly=PepperDash_Core.dll:b01629320e6c37f54b704d6f0e45c085 +MainAssembly=PepperDash_Core.dll:e116822f63c3419f6399d53e084bd358 MainAssemblyMinFirmwareVersion=1.007.0017 MainAssemblyResource=SimplSharpData.dat:315526abf906cded47fb0c7510266a7e ü +DependencySource=Newtonsoft.Json.Compact.dll:ea996aa2ec65aa1878e7c9d09e37a896 +DependencyPath=PepperDash_Core.clz:Newtonsoft.Json.Compact.dll +DependencyMainAssembly=Newtonsoft.Json.Compact.dll:ea996aa2ec65aa1878e7c9d09e37a896 +ü DependencySource=SimplSharpCustomAttributesInterface.dll:9c4b4d4c519b655af90016edca2d66b9 DependencyPath=PepperDash_Core.clz:SimplSharpCustomAttributesInterface.dll DependencyMainAssembly=SimplSharpCustomAttributesInterface.dll:9c4b4d4c519b655af90016edca2d66b9 diff --git a/Pepperdash Core/Pepperdash Core/bin/manifest.ser b/Pepperdash Core/Pepperdash Core/bin/manifest.ser index 3532caa..e89f716 100644 Binary files a/Pepperdash Core/Pepperdash Core/bin/manifest.ser and b/Pepperdash Core/Pepperdash Core/bin/manifest.ser differ diff --git a/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.dll b/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.dll new file mode 100644 index 0000000..769f0bd Binary files /dev/null and b/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.dll differ diff --git a/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.pdb b/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.pdb new file mode 100644 index 0000000..9cf3040 Binary files /dev/null and b/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.pdb differ diff --git a/Pepperdash Core/Pepperdash Core/obj/Debug/Pepperdash_Core.csproj.FileListAbsolute.txt b/Pepperdash Core/Pepperdash Core/obj/Debug/Pepperdash_Core.csproj.FileListAbsolute.txt index c0562ee..6f78127 100644 --- a/Pepperdash Core/Pepperdash Core/obj/Debug/Pepperdash_Core.csproj.FileListAbsolute.txt +++ b/Pepperdash Core/Pepperdash Core/obj/Debug/Pepperdash_Core.csproj.FileListAbsolute.txt @@ -8,3 +8,13 @@ C:\Users\hvolm\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepper C:\Users\hvolm\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\obj\Debug\ResolveAssemblyReference.cache C:\Users\hvolm\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\obj\Debug\PepperDash_Core.dll C:\Users\hvolm\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\obj\Debug\PepperDash_Core.pdb +C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.dll +C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\PepperDash_Core.pdb +C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\SimplSharpCustomAttributesInterface.dll +C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\SimplSharpHelperInterface.dll +C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\SimplSharpNewtonsoft.dll +C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\SimplSharpSQLHelperInterface.dll +C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\bin\SimplSharpReflectionInterface.dll +C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\obj\Debug\ResolveAssemblyReference.cache +C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\obj\Debug\PepperDash_Core.dll +C:\Users\hvolmer\Desktop\working\pepperdash-simplsharp-core\Pepperdash Core\Pepperdash Core\obj\Debug\PepperDash_Core.pdb diff --git a/Pepperdash Core/Pepperdash Core/obj/Debug/ResolveAssemblyReference.cache b/Pepperdash Core/Pepperdash Core/obj/Debug/ResolveAssemblyReference.cache new file mode 100644 index 0000000..bc60b6a Binary files /dev/null and b/Pepperdash Core/Pepperdash Core/obj/Debug/ResolveAssemblyReference.cache differ