Heartbeat problem resolved; much logging

This commit is contained in:
Heath Volmer
2017-12-05 16:53:20 -07:00
parent 864e65fe92
commit c3d455a7c2
9 changed files with 242 additions and 155 deletions

View File

@@ -16,7 +16,10 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
public class EssentialsConfig : BasicConfig public class EssentialsConfig : BasicConfig
{ {
[JsonProperty("system_url")]
public string SystemUrl { get; set; } public string SystemUrl { get; set; }
[JsonProperty("template_url")]
public string TemplateUrl { get; set; } public string TemplateUrl { get; set; }
public CotijaConfig Cotija { get; private set; } public CotijaConfig Cotija { get; private set; }
@@ -25,7 +28,7 @@ namespace PepperDash.Essentials
{ {
get get
{ {
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/templates\/(.*)#.*"); var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
string uuid = result.Groups[1].Value; string uuid = result.Groups[1].Value;
@@ -37,7 +40,7 @@ namespace PepperDash.Essentials
{ {
get get
{ {
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)#.*"); var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
string uuid = result.Groups[1].Value; string uuid = result.Groups[1].Value;

View File

@@ -4,5 +4,5 @@
[assembly: AssemblyCompany("PepperDash Technology Corp")] [assembly: AssemblyCompany("PepperDash Technology Corp")]
[assembly: AssemblyProduct("PepperDashEssentials")] [assembly: AssemblyProduct("PepperDashEssentials")]
[assembly: AssemblyCopyright("Copyright © PepperDash Technology Corp 2017")] [assembly: AssemblyCopyright("Copyright © PepperDash Technology Corp 2017")]
[assembly: AssemblyVersion("1.0.5.*")] [assembly: AssemblyVersion("1.0.6.*")]

View File

@@ -5,10 +5,13 @@ using System.Text;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using Newtonsoft.Json;
namespace PepperDash.Essentials namespace PepperDash.Essentials
{ {
public class CotijaConfig : DeviceConfig public class CotijaConfig : DeviceConfig
{ {
public string serverUrl { get; set; } [JsonProperty("serverUrl")]
public string ServerUrl { get; set; }
} }
} }

View File

@@ -22,17 +22,17 @@ namespace PepperDash.Essentials
CotijaConfig Config; CotijaConfig Config;
HttpClient Client; //HttpClient Client;
Dictionary<string, Object> ActionDictionary = new Dictionary<string, Object>(StringComparer.InvariantCultureIgnoreCase); Dictionary<string, Object> ActionDictionary = new Dictionary<string, Object>(StringComparer.InvariantCultureIgnoreCase);
Dictionary<string, CTimer> PushedActions = new Dictionary<string, CTimer>(); Dictionary<string, CTimer> PushedActions = new Dictionary<string, CTimer>();
CTimer ServerHeartbeat; CTimer ServerHeartbeatCheckTimer;
long ServerHeartbeatInterval = 20000; long ServerHeartbeatInterval = 20000;
CTimer ServerReconnect; CTimer ServerReconnectTimer;
long ServerReconnectInterval = 5000; long ServerReconnectInterval = 5000;
@@ -48,10 +48,15 @@ namespace PepperDash.Essentials
CotijaRooms = new List<CotijaEssentialsHuddleSpaceRoomBridge>(); CotijaRooms = new List<CotijaEssentialsHuddleSpaceRoomBridge>();
CrestronConsole.AddNewConsoleCommand(RegisterSystemToServer, "InitializeHttpClient", "Initializes a new HTTP client connection to a specified URL", ConsoleAccessLevelEnum.AccessOperator); //CrestronConsole.AddNewConsoleCommand(s => RegisterSystemToServer(),
CrestronConsole.AddNewConsoleCommand(DisconnectSseClient, "CloseHttpClient", "Closes the active HTTP client", ConsoleAccessLevelEnum.AccessOperator); // "CotiInitializeHttpClient", "Initializes a new HTTP client connection to a specified URL", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(DisconnectSseClient,
"CloseHttpClient", "Closes the active HTTP client", ConsoleAccessLevelEnum.AccessOperator);
AddPostActivationAction(() => RegisterSystemToServer(null)); CrestronConsole.AddNewConsoleCommand(AuthorizeSystem,
"cotijaauth", "Authorizes system to talk to cotija server", ConsoleAccessLevelEnum.AccessOperator);
AddPostActivationAction(() => RegisterSystemToServer());
} }
/// <summary> /// <summary>
@@ -67,12 +72,12 @@ namespace PepperDash.Essentials
} }
else else
{ {
Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary."); Debug.Console(1, this, "Cannot add action with key '{0}' because key already exists in ActionDictionary.", key);
} }
} }
/// <summary> /// <summary>
/// Removes and action from the dictionary /// Removes an action from the dictionary
/// </summary> /// </summary>
/// <param name="key"></param> /// <param name="key"></param>
public void RemoveAction(string key) public void RemoveAction(string key)
@@ -81,16 +86,58 @@ namespace PepperDash.Essentials
ActionDictionary.Remove(key); ActionDictionary.Remove(key);
} }
void ReconnectToServer(object o) void ReconnectToServerTimerCallback(object o)
{ {
RegisterSystemToServer(null); RegisterSystemToServer();
} }
/// <summary>
/// Verifies system connection with servers
/// </summary>
/// <param name="command"></param>
void AuthorizeSystem(string code)
{
if (string.IsNullOrEmpty(code))
{
CrestronConsole.ConsoleCommandResponse("Please enter a user code to authorize a system");
return;
}
var req = new HttpClientRequest();
string url = string.Format("http://{0}/api/system/grantcode/{1}", Config.ServerUrl, code);
Debug.Console(0, this, "Authorizing to: {0}", url);
if (string.IsNullOrEmpty(Config.ServerUrl))
{
CrestronConsole.ConsoleCommandResponse("Config URL address is not set. Check portal configuration");
return;
}
try
{
req.Url.Parse(url);
new HttpClient().DispatchAsync(req, (r, e) =>
{
if (r.Code == 200)
{
Debug.Console(0, this, "System authorized, sending config.");
RegisterSystemToServer();
}
else
Debug.Console(0, this, "HTTP Error {0} in authorizing system", r.Code);
});
}
catch (Exception e)
{
Debug.Console(0, this, "Error in authorizing: {0}", e);
}
}
/// <summary> /// <summary>
/// Registers the room with the server /// Registers the room with the server
/// </summary> /// </summary>
/// <param name="url">URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port"</param> /// <param name="url">URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port"</param>
void RegisterSystemToServer(string command) void RegisterSystemToServer()
{ {
try try
{ {
@@ -122,23 +169,22 @@ namespace PepperDash.Essentials
} }
else else
{ {
Client = new HttpClient(); var client = new HttpClient();
client.Verbose = true;
HttpClientRequest request = new HttpClientRequest(); client.KeepAlive = true;
Client.Verbose = true;
Client.KeepAlive = true;
SystemUuid = Essentials.ConfigReader.ConfigObject.SystemUuid; SystemUuid = Essentials.ConfigReader.ConfigObject.SystemUuid;
string url = string.Format("http://{0}/api/system/join/{1}", Config.serverUrl, SystemUuid); string url = string.Format("http://{0}/api/system/join/{1}", Config.ServerUrl, SystemUuid);
Debug.Console(1, this, "Sending config to {0}", url);
HttpClientRequest request = new HttpClientRequest();
request.Url.Parse(url); request.Url.Parse(url);
request.RequestType = RequestType.Post; request.RequestType = RequestType.Post;
request.Header.SetHeaderValue("Content-Type", "application/json"); request.Header.SetHeaderValue("Content-Type", "application/json");
request.ContentString = postBody; request.ContentString = postBody;
Client.DispatchAsync(request, PostConnectionCallback); client.DispatchAsync(request, PostConnectionCallback);
} }
} }
@@ -158,17 +204,14 @@ namespace PepperDash.Essentials
{ {
try try
{ {
if (Client == null) var client = new HttpClient();
Client = new HttpClient();
//HttpClient client = new HttpClient();
HttpClientRequest request = new HttpClientRequest(); HttpClientRequest request = new HttpClientRequest();
Client.Verbose = true; client.Verbose = true;
Client.KeepAlive = true; client.KeepAlive = true;
string url = string.Format("http://{0}/api/room/{1}/status", Config.serverUrl, string.Format("{0}--{1}", SystemUuid, room.Key)); string url = string.Format("http://{0}/api/system/{1}/status", Config.ServerUrl, SystemUuid);
request.Url.Parse(url); request.Url.Parse(url);
request.RequestType = RequestType.Post; request.RequestType = RequestType.Post;
@@ -180,10 +223,17 @@ namespace PepperDash.Essentials
request.ContentString = ignored; request.ContentString = ignored;
Debug.Console(1, this, "Posting to '{0}':\n{1}", url, request.ContentString); Debug.Console(1, this, "Posting to '{0}':\n{1}", url, request.ContentString);
try
{
client.DispatchAsync(request, (r, err) => { if (r != null) { Debug.Console(1, this, "Status Response Code: {0}", r.Code); } });
}
catch (Exception e)
{
Client.DispatchAsync(request, (r, err) => { if (r != null) { Debug.Console(1, this, "Status Response Code: {0}", r.Code); } }); Debug.Console(1, this, "PostToServer exception: {0}", e);
}
StartReconnectTimer(ServerReconnectInterval, ServerReconnectInterval); //ResetOrStartHearbeatTimer();
} }
catch(Exception e) catch(Exception e)
{ {
@@ -200,11 +250,11 @@ namespace PepperDash.Essentials
if(SseClient != null) if(SseClient != null)
SseClient.Disconnect(); SseClient.Disconnect();
if (ServerHeartbeat != null) if (ServerHeartbeatCheckTimer != null)
{ {
ServerHeartbeat.Stop(); ServerHeartbeatCheckTimer.Stop();
ServerHeartbeat = null; ServerHeartbeatCheckTimer = null;
} }
} }
@@ -219,11 +269,11 @@ namespace PepperDash.Essentials
{ {
if (resp != null && resp.Code == 200) if (resp != null && resp.Code == 200)
{ {
if(ServerReconnect != null) if(ServerReconnectTimer != null)
{ {
ServerReconnect.Stop(); ServerReconnectTimer.Stop();
ServerReconnect = null; ServerReconnectTimer = null;
} }
if (SseClient == null) if (SseClient == null)
@@ -249,40 +299,51 @@ namespace PepperDash.Essentials
/// Executes when we don't get a heartbeat message in time. Triggers reconnect. /// Executes when we don't get a heartbeat message in time. Triggers reconnect.
/// </summary> /// </summary>
/// <param name="o"></param> /// <param name="o"></param>
void HeartbeatExpired(object o) void HeartbeatExpiredTimerCallback(object o)
{ {
if (ServerHeartbeat != null) if (ServerHeartbeatCheckTimer != null)
{ {
Debug.Console(1, this, "Heartbeat Timer Expired."); Debug.Console(1, this, "Heartbeat Timer Expired.");
ServerHeartbeat.Stop(); ServerHeartbeatCheckTimer.Stop();
ServerHeartbeat = null; ServerHeartbeatCheckTimer = null;
} }
StartReconnectTimer(ServerReconnectInterval, ServerReconnectInterval); StartReconnectTimer();
} }
void StartReconnectTimer(long dueTime, long repeatTime) /// <summary>
///
/// </summary>
/// <param name="dueTime"></param>
/// <param name="repeatTime"></param>
void StartReconnectTimer()
{ {
// Start the reconnect timer // Start the reconnect timer
ServerReconnect = new CTimer(ReconnectToServer, null, dueTime, repeatTime); if (ServerReconnectTimer == null)
{
ServerReconnect.Reset(dueTime, repeatTime); ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, null, ServerReconnectInterval, ServerReconnectInterval);
Debug.Console(1, this, "Reconnect Timer Started.");
}
ServerReconnectTimer.Reset(ServerReconnectInterval, ServerReconnectInterval);
} }
void StartHearbeatTimer(long dueTime, long repeatTime) /// <summary>
///
/// </summary>
/// <param name="dueTime"></param>
/// <param name="repeatTime"></param>
void ResetOrStartHearbeatTimer()
{ {
if (ServerHeartbeat == null) if (ServerHeartbeatCheckTimer == null)
{ {
ServerHeartbeat = new CTimer(HeartbeatExpired, null, dueTime, repeatTime); ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval);
Debug.Console(2, this, "Heartbeat Timer Started."); Debug.Console(1, this, "Heartbeat Timer Started.");
} }
ServerHeartbeat.Reset(dueTime, repeatTime); ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval);
Debug.Console(2, this, "Heartbeat Timer Reset.");
} }
@@ -312,7 +373,7 @@ namespace PepperDash.Essentials
string uuid = Essentials.ConfigReader.ConfigObject.SystemUuid; string uuid = Essentials.ConfigReader.ConfigObject.SystemUuid;
SseClient.Url = string.Format("http://{0}/api/system/stream/{1}", Config.serverUrl, uuid); SseClient.Url = string.Format("http://{0}/api/system/stream/{1}", Config.ServerUrl, uuid);
SseClient.Connect(); SseClient.Connect();
} }
@@ -336,101 +397,102 @@ namespace PepperDash.Essentials
if (type == "hello") if (type == "hello")
{ {
StartHearbeatTimer(ServerHeartbeatInterval, ServerHeartbeatInterval); ResetOrStartHearbeatTimer();
} }
else if (type == "/system/heartbeat") else if (type == "/system/heartbeat")
{ {
StartHearbeatTimer(ServerHeartbeatInterval, ServerHeartbeatInterval); ResetOrStartHearbeatTimer();
} }
else if (type == "close") else if (type == "close")
{ {
SseClient.Disconnect(); SseClient.Disconnect();
// Start the reconnect timer ServerHeartbeatCheckTimer.Stop();
ServerReconnect = new CTimer(ConnectSseClient, null, ServerReconnectInterval, ServerReconnectInterval); // Start the reconnect timer
StartReconnectTimer();
//ServerReconnectTimer = new CTimer(ConnectSseClient, null, ServerReconnectInterval, ServerReconnectInterval);
//ServerReconnectTimer.Reset(ServerReconnectInterval, ServerReconnectInterval);
}
else
{
// Check path against Action dictionary
if (ActionDictionary.ContainsKey(type))
{
var action = ActionDictionary[type];
ServerReconnect.Reset(ServerReconnectInterval, ServerReconnectInterval); if (action is Action)
} {
else (action as Action)();
{ }
// Check path against Action dictionary else if (action is PressAndHoldAction)
if (ActionDictionary.ContainsKey(type)) {
{ var stateString = messageObj["content"]["state"].Value<string>();
var action = ActionDictionary[type];
if (action is Action) // Look for a button press event
{ if (!string.IsNullOrEmpty(stateString))
(action as Action)(); {
} switch (stateString)
else if (action is PressAndHoldAction) {
{ case "true":
var stateString = messageObj["content"]["state"].Value<string>(); {
if (!PushedActions.ContainsKey(type))
{
PushedActions.Add(type, new CTimer(o =>
{
(action as PressAndHoldAction)(false);
PushedActions.Remove(type);
}, null, ButtonHeartbeatInterval, ButtonHeartbeatInterval));
}
// Maybe add an else to reset the timer
break;
}
case "held":
{
if (!PushedActions.ContainsKey(type))
{
PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval);
}
return;
}
case "false":
{
if (PushedActions.ContainsKey(type))
{
PushedActions[type].Stop();
PushedActions.Remove(type);
}
break;
}
}
// Look for a button press event (action as PressAndHoldAction)(stateString == "true");
if (!string.IsNullOrEmpty(stateString)) }
{ }
switch (stateString) else if (action is Action<bool>)
{ {
case "true": var stateString = messageObj["content"]["state"].Value<string>();
{
if (!PushedActions.ContainsKey(type))
{
PushedActions.Add(type, new CTimer(o =>
{
(action as PressAndHoldAction)(false);
PushedActions.Remove(type);
}, null, ButtonHeartbeatInterval, ButtonHeartbeatInterval));
}
// Maybe add an else to reset the timer
break;
}
case "held":
{
if (!PushedActions.ContainsKey(type))
{
PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval);
}
return;
}
case "false":
{
if (PushedActions.ContainsKey(type))
{
PushedActions[type].Stop();
PushedActions.Remove(type);
}
break;
}
}
(action as PressAndHoldAction)(stateString == "true"); if (!string.IsNullOrEmpty(stateString))
} {
} (action as Action<bool>)(stateString == "true");
else if (action is Action<bool>) }
{ }
var stateString = messageObj["content"]["state"].Value<string>(); else if (action is Action<ushort>)
{
(action as Action<ushort>)(messageObj["content"]["value"].Value<ushort>());
}
else if (action is Action<string>)
{
(action as Action<string>)(messageObj["content"]["value"].Value<string>());
}
else if (action is Action<SourceSelectMessageContent>)
{
(action as Action<SourceSelectMessageContent>)(messageObj["content"]
.ToObject<SourceSelectMessageContent>());
}
}
if (!string.IsNullOrEmpty(stateString)) }
{
(action as Action<bool>)(stateString == "true");
}
}
else if (action is Action<ushort>)
{
(action as Action<ushort>)(messageObj["content"]["value"].Value<ushort>());
}
else if (action is Action<string>)
{
(action as Action<string>)(messageObj["content"]["value"].Value<string>());
}
else if (action is Action<SourceSelectMessageContent>)
{
(action as Action<SourceSelectMessageContent>)(messageObj["content"]
.ToObject<SourceSelectMessageContent>());
}
}
}
} }
catch (Exception err) catch (Exception err)

View File

@@ -207,11 +207,23 @@ namespace PepperDash.Essentials
/// <summary> /// <summary>
/// 1252 /// 1252
/// </summary> /// </summary>
public const uint VCRemoteViewTogglePress = 1252; public const uint VCLayoutTogglePress = 1252;
/// <summary> /// <summary>
/// 1253 /// 1253
/// </summary> /// </summary>
public const uint VCSelfViewPipTogglePress = 1253; public const uint VCSelfViewPipTogglePress = 1253;
/// <summary>
/// 1254
/// </summary>
public const uint VCLayoutToggleEnable = 1254;
/// <summary>
/// 1255
/// </summary>
public const uint VCMinMaxPress = 1255;
/// <summary>
/// 1256
/// </summary>
public const uint VCMinMaxEnable = 1256;
//****************************************************** //******************************************************

View File

@@ -73,6 +73,7 @@ namespace PepperDash.Essentials.UIDrivers
true, 3100); true, 3100);
MenuList.SetFeedback(1, true); // initial fb MenuList.SetFeedback(1, true); // initial fb
ushort count = 0;
MenuList.SetItemMainText(1, "System Status"); MenuList.SetItemMainText(1, "System Status");
MenuList.SetItemButtonAction(1, b => { MenuList.SetItemButtonAction(1, b => {
@@ -84,19 +85,25 @@ namespace PepperDash.Essentials.UIDrivers
MenuList.SetItemButtonAction(2, b => { MenuList.SetItemButtonAction(2, b => {
if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechDisplayControlsVisible); if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechDisplayControlsVisible);
MenuList.SetFeedback(2, true); MenuList.SetFeedback(2, true);
});
MenuList.SetItemMainText(3, "Panel Setup");
MenuList.SetItemButtonAction(3, b => {
if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechPanelSetupVisible);
MenuList.SetFeedback(3, true);
}); });
MenuList.Count = 3;
count = 2;
// Don't show panel setup on iPad or xpanel
if (TriList is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button)
{
count++;
MenuList.SetItemMainText(count, "Panel Setup");
MenuList.SetItemButtonAction(count, b =>
{
if (b) PagesInterlock.ShowInterlocked(UIBoolJoin.TechPanelSetupVisible);
MenuList.SetFeedback(count, true);
});
}
MenuList.Count = count;
BuildStatusList(); BuildStatusList();
BuildDisplayList(); BuildDisplayList();
SetupPinModal(); SetupPinModal();
} }

View File

@@ -751,7 +751,7 @@ namespace PepperDash.Essentials.UIDrivers.VC
var lc = Codec as IHasCodecLayouts; var lc = Codec as IHasCodecLayouts;
if (lc != null) if (lc != null)
{ {
TriList.SetSigFalseAction(UIBoolJoin.VCRemoteViewTogglePress, lc.LocalLayoutToggle); TriList.SetSigFalseAction(UIBoolJoin.VCLayoutTogglePress, lc.LocalLayoutToggle);
lc.LocalLayoutFeedback.LinkInputSig(TriList.StringInput[UIStringJoin.VCLayoutModeText]); lc.LocalLayoutFeedback.LinkInputSig(TriList.StringInput[UIStringJoin.VCLayoutModeText]);
} }
} }