Changes to GenericTcpIpClient to accomodate S+ wrapper; Moved Web API and JsonToSimpl in

This commit is contained in:
Heath Volmer
2017-01-04 14:50:18 -07:00
parent 755010251f
commit 5fb4150d21
38 changed files with 1711 additions and 29 deletions

View File

@@ -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;
}
}
}

View File

@@ -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<JsonToSimplMaster> Masters = new List<JsonToSimplMaster>();
/// <summary>
/// Adds a file master. If the master's key or filename is equivalent to any existing
/// master, this will fail
/// </summary>
/// <param name="master">New master to add</param>
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);
}
}
/// <summary>
/// Gets a master by its key. Case-insensitive
/// </summary>
public static JsonToSimplMaster GetMasterByFile(string file)
{
return Masters.FirstOrDefault(m => m.UniqueID.Equals(file, StringComparison.OrdinalIgnoreCase));
}
}
}

View File

@@ -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
/// <summary>
///
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
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<string>()
.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;
}
}
}

View File

@@ -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<BoolChangeEventArgs> BoolChange;
public event EventHandler<UshrtChangeEventArgs> UShortChange;
public event EventHandler<StringChangeEventArgs> StringChange;
public SPlusValuesDelegate GetAllValuesDelegate { get; set; }
/// <summary>
/// Use a callback to reduce task switch/threading
/// </summary>
public SPlusValuesDelegate SetAllPathsDelegate { get; set; }
public string Key { get; protected set; }
/// <summary>
/// This will be prepended to all paths to allow path swapping or for more organized
/// sub-paths
/// </summary>
public string PathPrefix { get; protected set; }
/// <summary>
/// This is added to the end of all paths
/// </summary>
public string PathSuffix { get; protected set; }
protected JsonToSimplMaster Master;
// The sent-in JPaths for the various types
protected Dictionary<ushort, string> BoolPaths = new Dictionary<ushort, string>();
protected Dictionary<ushort, string> UshortPaths = new Dictionary<ushort, string>();
protected Dictionary<ushort, string> StringPaths = new Dictionary<ushort, string>();
/// <summary>
/// Call this before doing anything else
/// </summary>
/// <param name="file"></param>
/// <param name="key"></param>
/// <param name="pathPrefix"></param>
/// <param name="pathSuffix"></param>
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);
}
/// <summary>
/// Set the JPath to evaluate for a given bool out index.
/// </summary>
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;
}
/// <summary>
/// Set the JPath for a ushort out index.
/// </summary>
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;
}
/// <summary>
/// Set the JPath for a string output index.
/// </summary>
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;
}
/// <summary>
/// Evalutates all outputs with defined paths. called by S+ when paths are ready to process
/// and by Master when file is read.
/// </summary>
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);
}
/// <summary>
/// Processes a bool property, converting to bool, firing off a BoolChange event
/// </summary>
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);
}
/// <summary>
/// Processes the given path.
/// </summary>
/// <param name="path">JPath formatted path to the desired property</param>
/// <param name="response">The string value of the property, or a default value if it
/// doesn't exist</param>
/// <returns> This will return false in the case that EvaulateAllOnJsonChange
/// is false and the path does not evaluate to a property in the incoming JSON. </returns>
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<string>();
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
/// <summary>
/// Called from Master to read inputs and update their values in master JObject
/// Callback should hit one of the following four methods
/// </summary>
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);
}
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
}
}

View File

@@ -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
{
/// <summary>
/// Sets the filepath as well as registers this with the Global.Masters list
/// </summary>
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();
/*****************************************************************************************/
/// <summary>
/// SIMPL+ default constructor.
/// </summary>
public JsonToSimplFileMaster()
{
}
/// <summary>
/// Read, evaluate and udpate status
/// </summary>
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<string, JValue>();
// 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;
}
}
}
}
}
}

View File

@@ -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()
{
}
}
}

View File

@@ -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<string> SaveCallback { get; set; }
/*****************************************************************************************/
/// <summary>
/// SIMPL+ default constructor.
/// </summary>
public JsonToSimplGenericMaster()
{
}
/// <summary>
/// Loads in JSON and triggers evaluation on all children
/// </summary>
/// <param name="json"></param>
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);
}
}
/// <summary>
/// Loads JSON into JsonObject, but does not trigger evaluation by children
/// </summary>
/// <param name="json"></param>
public void SetJsonWithoutEvaluating(string json)
{
try
{
JsonObject = JObject.Parse(json);
}
catch (Exception e)
{
Debug.Console(0, this, "JSON parsing failed:\r{0}", e);
}
}
/// <summary>
///
/// </summary>
public override void Save()
{
// this code is duplicated in the other masters!!!!!!!!!!!!!
UnsavedValues = new Dictionary<string, JValue>();
// 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]);

View File

@@ -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<BoolChangeEventArgs> BoolChange;
public event EventHandler<UshrtChangeEventArgs> UshrtChange;
public event EventHandler<StringChangeEventArgs> StringChange;
protected List<JsonToSimplChildObjectBase> Children = new List<JsonToSimplChildObjectBase>();
/*****************************************************************************************/
/// <summary>
/// Mirrors the Unique ID for now.
/// </summary>
public string Key { get { return UniqueID; } }
public string UniqueID { get; protected set; }
/// <summary>
/// Merely for use in debug messages
/// </summary>
public string DebugName
{
get { return _DebugName; }
set { if (DebugName == null) _DebugName = ""; else _DebugName = value; }
}
string _DebugName = "";
/// <summary>
/// This will be prepended to all paths to allow path swapping or for more organized
/// sub-paths
/// </summary>
public string PathPrefix { get; set; }
/// <summary>
/// This is added to the end of all paths
/// </summary>
public string PathSuffix { get; set; }
/// <summary>
/// Enables debugging output to the console. Certain error messages will be logged to the
/// system's error log regardless of this setting
/// </summary>
public bool DebugOn { get; set; }
/// <summary>
/// Ushort helper for Debug property
/// </summary>
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<string, JValue> UnsavedValues = new Dictionary<string, JValue>();
/*****************************************************************************************/
/// <summary>
/// SIMPL+ default constructor.
/// </summary>
public JsonToSimplMaster()
{
}
/// <summary>
/// Sets up class - overriding methods should always call this.
/// </summary>
/// <param name="uniqueId"></param>
public virtual void Initialize(string uniqueId)
{
UniqueID = uniqueId;
J2SGlobal.AddMaster(this); // Should not re-add
}
/// <summary>
/// Adds a child "module" to this master
/// </summary>
/// <param name="child"></param>
public void AddChild(JsonToSimplChildObjectBase child)
{
if (Children.Contains(child)) return;
Children.Add(child);
}
/// <summary>
/// Called from the child to add changed or new values for saving
/// </summary>
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);
}
}
}
}

View File

@@ -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()
// {
// }
// }
//}