Merge remote-tracking branch 'origin/bugfix/mob-470' into feature/ecs-967

# Conflicts:
#	PepperDashEssentials/AppServer/CotijaSystemController.cs
#	PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs
#	PepperDashEssentials/ControlSystem.cs
#	PepperDashEssentials/OTHER/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs
#	PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs
#	PepperDashEssentials/Properties/AssemblyInfo.cs
#	PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs
#	devjson commands.json
This commit is contained in:
Neil Dorin
2018-12-12 13:34:32 -07:00
12 changed files with 2045 additions and 2106 deletions

View File

@@ -15,7 +15,6 @@ using Newtonsoft.Json.Linq;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Cotija; using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials namespace PepperDash.Essentials
@@ -24,6 +23,8 @@ namespace PepperDash.Essentials
{ {
WebSocketClient WSClient; WebSocketClient WSClient;
//bool LinkUp;
/// <summary> /// <summary>
/// Prevents post operations from stomping on each other and getting lost /// Prevents post operations from stomping on each other and getting lost
/// </summary> /// </summary>
@@ -83,12 +84,33 @@ namespace PepperDash.Essentials
CrestronConsole.AddNewConsoleCommand(TestHttpRequest, CrestronConsole.AddNewConsoleCommand(TestHttpRequest,
"mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator); "mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(PrintActionDictionaryPaths, "showactionpaths", "Prints the paths in teh Action Dictionary", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(PrintActionDictionaryPaths, "mobileshowactionpaths",
"Prints the paths in the Action Dictionary", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => ConnectWebsocketClient(), "mobileconnect",
"Forces connect of websocket", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CleanUpWebsocketClient(), "mobiledisco",
"Disconnects websocket", ConsoleAccessLevelEnum.AccessOperator);
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
} }
/// <summary>
///
/// </summary>
/// <param name="ethernetEventArgs"></param>
void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs args)
{
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Ethernet status change, port {0}: {1}",
args.EthernetAdapter, args.EthernetEventType);
if (args.EthernetEventType == eEthernetEventType.LinkDown && WSClient != null && args.EthernetAdapter == WSClient.EthernetAdapter)
{
CleanUpWebsocketClient();
}
}
/// <summary> /// <summary>
/// Sends message to server to indicate the system is shutting down /// Sends message to server to indicate the system is shutting down
/// </summary> /// </summary>
@@ -97,11 +119,7 @@ namespace PepperDash.Essentials
{ {
if (programEventType == eProgramStatusEventType.Stopping && WSClient.Connected) if (programEventType == eProgramStatusEventType.Stopping && WSClient.Connected)
{ {
SendMessageToServer(JObject.FromObject( new CleanUpWebsocketClient();
{
type = "/system/close"
}));
} }
} }
@@ -158,6 +176,7 @@ namespace PepperDash.Essentials
else else
{ {
Debug.Console(0, this, "Adding room bridge and sending configuration"); Debug.Console(0, this, "Adding room bridge and sending configuration");
SystemUuid = ConfigReader.ConfigObject.SystemUuid;
RegisterSystemToServer(); RegisterSystemToServer();
} }
} }
@@ -170,6 +189,7 @@ namespace PepperDash.Essentials
void bridge_ConfigurationIsReady(object sender, EventArgs e) void bridge_ConfigurationIsReady(object sender, EventArgs e)
{ {
Debug.Console(1, this, "Bridge ready. Registering"); Debug.Console(1, this, "Bridge ready. Registering");
SystemUuid = ConfigReader.ConfigObject.SystemUuid;
// send the configuration object to the server // send the configuration object to the server
RegisterSystemToServer(); RegisterSystemToServer();
} }
@@ -189,6 +209,8 @@ namespace PepperDash.Essentials
/// <param name="command"></param> /// <param name="command"></param>
void AuthorizeSystem(string code) void AuthorizeSystem(string code)
{ {
SystemUuid = ConfigReader.ConfigObject.SystemUuid;
if (string.IsNullOrEmpty(SystemUuid)) if (string.IsNullOrEmpty(SystemUuid))
{ {
CrestronConsole.ConsoleCommandResponse("System does not have a UUID. Please ensure proper portal-format configuration is loaded and restart."); CrestronConsole.ConsoleCommandResponse("System does not have a UUID. Please ensure proper portal-format configuration is loaded and restart.");
@@ -221,6 +243,7 @@ namespace PepperDash.Essentials
if (r.Code == 200) if (r.Code == 200)
{ {
Debug.Console(0, "System authorized, sending config."); Debug.Console(0, "System authorized, sending config.");
#warning This registration may need to wait for config ready. Maybe.
RegisterSystemToServer(); RegisterSystemToServer();
} }
else if (r.Code == 404) else if (r.Code == 404)
@@ -281,58 +304,104 @@ namespace PepperDash.Essentials
/// <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() void RegisterSystemToServer()
{ {
ConnectWebsocketClient();
var ready = RegisterLockEvent.Wait(20000);
if (!ready)
{
Debug.Console(1, this, "RegisterSystemToServer failed to enter after 20 seconds. Ignoring");
return;
}
RegisterLockEvent.Reset();
try
{
var confObject = ConfigReader.ConfigObject;
confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
var version = Assembly.GetExecutingAssembly().GetName().Version;
confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
string postBody = JsonConvert.SerializeObject(confObject);
SystemUuid = confObject.SystemUuid;
if (string.IsNullOrEmpty(postBody))
{
Debug.Console(1, this, "ERROR: Config body is empty. Cannot register with server.");
}
else
{
var regClient = new HttpClient();
regClient.Verbose = true;
regClient.KeepAlive = true;
string url = string.Format("http://{0}/api/system/join/{1}", Config.ServerUrl, SystemUuid);
Debug.Console(1, this, "Joining server at {0}", url);
HttpClientRequest request = new HttpClientRequest();
request.Url.Parse(url);
request.RequestType = RequestType.Post;
request.Header.SetHeaderValue("Content-Type", "application/json");
request.ContentString = postBody;
var err = regClient.DispatchAsync(request, RegistrationConnectionCallback);
}
}
catch (Exception e)
{
Debug.Console(0, this, "ERROR: Initilizing Room: {0}", e);
RegisterLockEvent.Set();
StartReconnectTimer();
}
} }
/// <summary>
/// Connects the Websocket Client
/// </summary>
/// <param name="o"></param>
void ConnectWebsocketClient()
{
Debug.Console(1, this, "Initializing Stream client to server.");
if (WSClient != null)
{
Debug.Console(1, this, "Cleaning up previous socket");
CleanUpWebsocketClient();
}
WSClient = new WebSocketClient();
WSClient.URL = string.Format("wss://{0}/system/join/{1}", Config.ServerUrl, this.SystemUuid);
WSClient.ConnectionCallBack = Websocket_ConnectCallback;
WSClient.ConnectAsync();
}
/// <summary>
///
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
int Websocket_ConnectCallback(WebSocketClient.WEBSOCKET_RESULT_CODES code)
{
if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
{
StopServerReconnectTimer();
Debug.Console(1, this, "Websocket connected");
WSClient.DisconnectCallBack = Websocket_DisconnectCallback;
WSClient.SendCallBack = Websocket_SendCallback;
WSClient.ReceiveCallBack = Websocket_ReceiveCallback;
WSClient.ReceiveAsync();
SendMessageObjectToServer(new
{
type = "hello"
});
}
else
{
if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_HTTP_HANDSHAKE_TOKEN_ERROR)
{
// This is the case when app server is running behind a websever and app server is down
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failed. Check that app server is running behind web server");
}
else if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SOCKET_CONNECTION_FAILED)
{
// this will be the case when webserver is unreachable
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failed");
}
else
{
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failure: {0}", code);
}
StartServerReconnectTimer();
}
return 0;
}
/// <summary>
/// After a "hello" from the server, sends config and stuff
/// </summary>
void SendInitialMessage()
{
Debug.Console(1, this, "Sending initial join message");
var confObject = ConfigReader.ConfigObject;
confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
var version = Assembly.GetExecutingAssembly().GetName().Version;
confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
var msg = new
{
type = "join",
content = new
{
config = confObject
}
};
SendMessageObjectToServer(msg);
}
/// <summary>
/// Sends any object type to server
/// </summary>
/// <param name="o"></param>
public void SendMessageObjectToServer(object o)
{
SendMessageToServer(JObject.FromObject(o));
}
/// <summary> /// <summary>
/// Sends a message to the server from a room /// Sends a message to the server from a room
/// </summary> /// </summary>
@@ -340,78 +409,67 @@ namespace PepperDash.Essentials
/// <param name="o">object to be serialized and sent in post body</param> /// <param name="o">object to be serialized and sent in post body</param>
public void SendMessageToServer(JObject o) public void SendMessageToServer(JObject o)
{ {
if (WSClient != null && WSClient.Connected) if (WSClient != null && WSClient.Connected)
{ {
string message = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); string message = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
Debug.Console(1, this, "Message TX: {0}", message); Debug.Console(1, this, "Message TX: {0}", message);
var messageBytes = System.Text.Encoding.UTF8.GetBytes(message); var messageBytes = System.Text.Encoding.UTF8.GetBytes(message);
WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME); var result = WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME);
//WSClient.SendAsync(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME); if (result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
{
Debug.Console(1, this, "Socket send result error: {0}", result);
}
} }
else if (!WSClient.Connected)
{
Debug.Console(1, this, "Cannot send. Not connected {0}");
}
} }
/// <summary> /// <summary>
/// Disconnects the SSE Client and stops the heartbeat timer /// Disconnects the SSE Client and stops the heartbeat timer
/// </summary> /// </summary>
/// <param name="command"></param> /// <param name="command"></param>
void DisconnectStreamClient(string command) void CleanUpWebsocketClient()
{ {
//if(SseClient != null) Debug.Console(1, this, "Disconnecting websocket");
// SseClient.Disconnect(); if (WSClient != null)
{
if (WSClient != null && WSClient.Connected) WSClient.SendCallBack = null;
WSClient.Disconnect(); WSClient.ReceiveCallBack = null;
WSClient.ConnectionCallBack = null;
if (ServerHeartbeatCheckTimer != null) WSClient.DisconnectCallBack = null;
{ if (WSClient.Connected)
ServerHeartbeatCheckTimer.Stop(); {
WSClient.Disconnect();
ServerHeartbeatCheckTimer = null; }
} WSClient = null;
}
} }
/// <summary> /// <summary>
/// The callback that fires when we get a response from our registration attempt ///
/// </summary> /// </summary>
/// <param name="resp"></param> /// <param name="dueTime"></param>
/// <param name="err"></param> /// <param name="repeatTime"></param>
void RegistrationConnectionCallback(HttpClientResponse resp, HTTP_CALLBACK_ERROR err) void StartServerReconnectTimer()
{ {
CheckHttpDebug(resp, err); StopServerReconnectTimer();
Debug.Console(1, this, "RegistrationConnectionCallback: {0}", err); ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, ServerReconnectInterval);
try Debug.Console(1, this, "Reconnect Timer Started.");
{ }
if (resp != null && resp.Code == 200)
{
if(ServerReconnectTimer != null)
{
ServerReconnectTimer.Stop();
ServerReconnectTimer = null;
}
// Success here! /// <summary>
ConnectStreamClient(); /// Does what it says
} /// </summary>
else void StopServerReconnectTimer()
{ {
if (resp != null) if (ServerReconnectTimer != null)
Debug.Console(1, this, "Response from server: {0}\n{1}", resp.Code, err); {
else ServerReconnectTimer.Stop();
{ ServerReconnectTimer = null;
Debug.Console(1, this, "Null response received from server."); }
} }
StartReconnectTimer();
}
}
catch (Exception e)
{
Debug.Console(1, this, "Error Initializing Stream Client: {0}", e);
StartReconnectTimer();
}
RegisterLockEvent.Set();
}
/// <summary> /// <summary>
/// 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.
@@ -425,23 +483,8 @@ namespace PepperDash.Essentials
ServerHeartbeatCheckTimer.Stop(); ServerHeartbeatCheckTimer.Stop();
ServerHeartbeatCheckTimer = null; ServerHeartbeatCheckTimer = null;
} }
StartReconnectTimer(); CleanUpWebsocketClient();
} StartServerReconnectTimer();
/// <summary>
///
/// </summary>
/// <param name="dueTime"></param>
/// <param name="repeatTime"></param>
void StartReconnectTimer()
{
// Start the reconnect timer
if (ServerReconnectTimer == null)
{
ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, null, ServerReconnectInterval, ServerReconnectInterval);
Debug.Console(1, this, "Reconnect Timer Started.");
}
ServerReconnectTimer.Reset(ServerReconnectInterval, ServerReconnectInterval);
} }
/// <summary> /// <summary>
@@ -451,37 +494,42 @@ namespace PepperDash.Essentials
/// <param name="repeatTime"></param> /// <param name="repeatTime"></param>
void ResetOrStartHearbeatTimer() void ResetOrStartHearbeatTimer()
{ {
if (ServerHeartbeatCheckTimer == null) if (ServerHeartbeatCheckTimer == null)
{
ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval);
Debug.Console(1, this, "Heartbeat Timer Started.");
}
ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval);
}
/// <summary>
/// Connects the SSE Client
/// </summary>
/// <param name="o"></param>
void ConnectStreamClient()
{
Debug.Console(0, this, "Initializing Stream client to server.");
if (WSClient == null)
{ {
WSClient = new WebSocketClient(); ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval);
Debug.Console(1, this, "Heartbeat Timer Started.");
}
else
{
ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval);
} }
WSClient.URL = string.Format("wss://{0}/system/join/{1}", Config.ServerUrl, this.SystemUuid);
WSClient.Connect();
Debug.Console(0, this, "Websocket connected");
WSClient.ReceiveCallBack = WebsocketReceiveCallback;
//WSClient.SendCallBack = WebsocketSendCallback;
WSClient.ReceiveAsync();
} }
/// <summary>
/// Waits two and goes again
/// </summary>
void ReconnectStreamClient()
{
new CTimer(o => ConnectWebsocketClient(), 2000);
}
/// <summary>
///
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
int Websocket_DisconnectCallback(WebSocketClient.WEBSOCKET_RESULT_CODES code, object o)
{
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Websocket disconnected with code: {0}", code);
if (ServerHeartbeatCheckTimer != null)
ServerHeartbeatCheckTimer.Stop();
// Start the reconnect timer
StartServerReconnectTimer();
return 0;
}
/// <summary> /// <summary>
/// Resets reconnect timer and updates usercode /// Resets reconnect timer and updates usercode
/// </summary> /// </summary>
@@ -514,12 +562,26 @@ namespace PepperDash.Essentials
{ {
if (HttpDebugEnabled) if (HttpDebugEnabled)
{ {
Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------"); try
Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl.ToString()); {
Debug.Console(0, this, "HTTP Response 'error' {0}", e); Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------");
Debug.Console(0, this, "HTTP Response code: {0}", r.Code); if (r != null)
Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString); {
Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------"); Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl != null ? r.ResponseUrl.ToString() : "NONE");
Debug.Console(0, this, "HTTP Response code: {0}", r.Code);
Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString);
}
else
{
Debug.Console(0, this, "No HTTP response");
}
Debug.Console(0, this, "HTTP Response 'error' {0}", e);
Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------");
}
catch (Exception ex)
{
Debug.Console(0, this, "HttpDebugError: {0}", ex);
}
} }
} }
@@ -530,14 +592,28 @@ namespace PepperDash.Essentials
/// <param name="length"></param> /// <param name="length"></param>
/// <param name="opcode"></param> /// <param name="opcode"></param>
/// <param name="err"></param> /// <param name="err"></param>
int WebsocketReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode, int Websocket_ReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode,
WebSocketClient.WEBSOCKET_RESULT_CODES err) WebSocketClient.WEBSOCKET_RESULT_CODES err)
{ {
var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length); if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME)
if(rx.Length > 0) {
ParseStreamRx(rx); var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length);
WSClient.ReceiveAsync(); if (rx.Length > 0)
return 1; ParseStreamRx(rx);
WSClient.ReceiveAsync();
}
else if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__CLOSE)
{
Debug.Console(1, this, "Websocket disconnect received from remote");
CleanUpWebsocketClient();
}
else
{
Debug.Console(1, this, "websocket rx opcode/err {0}/{1}", opcode, err);
WSClient.ReceiveAsync();
}
return 0;
} }
/// <summary> /// <summary>
@@ -545,11 +621,11 @@ namespace PepperDash.Essentials
/// </summary> /// </summary>
/// <param name="result"></param> /// <param name="result"></param>
/// <returns></returns> /// <returns></returns>
int WebsocketSendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result) int Websocket_SendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result)
{ {
Debug.Console(1, this, "SendCallback result: {0}", result); if(result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SendCallback questionable result: {0}", result);
return 1; return 1;
} }
/// <summary> /// <summary>
@@ -562,7 +638,7 @@ namespace PepperDash.Essentials
if(string.IsNullOrEmpty(message)) if(string.IsNullOrEmpty(message))
return; return;
Debug.Console(1, this, "Message RX: '{0}'", message); Debug.Console(1, this, "Message RX: {0}", message);
try try
{ {
var messageObj = JObject.Parse(message); var messageObj = JObject.Parse(message);
@@ -571,6 +647,7 @@ namespace PepperDash.Essentials
if (type == "hello") if (type == "hello")
{ {
SendInitialMessage();
ResetOrStartHearbeatTimer(); ResetOrStartHearbeatTimer();
} }
else if (type == "/system/heartbeat") else if (type == "/system/heartbeat")
@@ -579,11 +656,11 @@ namespace PepperDash.Essentials
} }
else if (type == "close") else if (type == "close")
{ {
WSClient.Disconnect(); Debug.Console(1, this, "Received close message from server.");
// DisconnectWebsocketClient();
ServerHeartbeatCheckTimer.Stop(); if (ServerHeartbeatCheckTimer != null)
// Start the reconnect timer ServerHeartbeatCheckTimer.Stop();
StartReconnectTimer();
} }
else else
{ {
@@ -620,7 +697,7 @@ namespace PepperDash.Essentials
} }
case "held": case "held":
{ {
if (!PushedActions.ContainsKey(type)) if (PushedActions.ContainsKey(type))
{ {
PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval); PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval);
} }

View File

@@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.EthernetCommunication;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common.Codec;
namespace PepperDash.Essentials.AppServer.Messengers
{
public class Ddvc01AtcMessenger : MessengerBase
{
BasicTriList EISC;
const uint BDialHangup = 221;
const uint BIncomingAnswer = 251;
const uint BIncomingReject = 252;
const uint BSpeedDial1 = 241;
const uint BSpeedDial2 = 242;
const uint BSpeedDial3 = 243;
const uint BSpeedDial4 = 244;
/// <summary>
/// 201
/// </summary>
const uint SCurrentDialString = 201;
/// <summary>
/// 211
/// </summary>
const uint SCurrentCallString = 211;
/// <summary>
/// 221
/// </summary>
const uint SHookState = 221;
/// <summary>
///
/// </summary>
Dictionary<string, uint> DTMFMap = new Dictionary<string, uint>
{
{ "1", 201 },
{ "2", 202 },
{ "3", 203 },
{ "4", 204 },
{ "5", 205 },
{ "6", 206 },
{ "7", 207 },
{ "8", 208 },
{ "9", 209 },
{ "0", 210 },
{ "*", 211 },
{ "#", 212 },
};
CodecActiveCallItem CurrentCallItem;
/// <summary>
///
/// </summary>
/// <param name="eisc"></param>
/// <param name="messagePath"></param>
public Ddvc01AtcMessenger(BasicTriList eisc, string messagePath)
: base(messagePath)
{
EISC = eisc;
CurrentCallItem = new CodecActiveCallItem();
CurrentCallItem.Type = eCodecCallType.Audio;
CurrentCallItem.Id = "-audio-";
}
/// <summary>
///
/// </summary>
void SendFullStatus()
{
this.PostStatusMessage(new
{
calls = GetCurrentCallList(),
callStatus = EISC.GetString(SHookState),
currentCallString = EISC.GetString(SCurrentCallString),
currentDialString = EISC.GetString(SCurrentDialString),
});
}
/// <summary>
///
/// </summary>
/// <param name="appServerController"></param>
protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
{
Action<object> send = this.PostStatusMessage;
EISC.SetStringSigAction(SCurrentDialString, s => send(new { currentDialString = s }));
EISC.SetStringSigAction(SHookState, s =>
{
CurrentCallItem.Status = (eCodecCallStatus)Enum.Parse(typeof(eCodecCallStatus), s, true);
GetCurrentCallList();
send(new
{
calls = GetCurrentCallList(),
callStatus = s
});
});
EISC.SetStringSigAction(SCurrentCallString, s =>
{
CurrentCallItem.Name = s;
CurrentCallItem.Number = s;
send(new
{
calls = GetCurrentCallList(),
currentCallString = s
});
});
// Add press and holds using helper
Action<string, uint> addPHAction = (s, u) =>
AppServerController.AddAction(MessagePath + s, new PressAndHoldAction(b => EISC.SetBool(u, b)));
// Add straight pulse calls
Action<string, uint> addAction = (s, u) =>
AppServerController.AddAction(MessagePath + s, new Action(() => EISC.PulseBool(u, 100)));
addAction("/endCall", BDialHangup);
addAction("/incomingAnswer", BIncomingAnswer);
addAction("/incomingReject", BIncomingReject);
addAction("/speedDial1", BSpeedDial1);
addAction("/speedDial2", BSpeedDial2);
addAction("/speedDial3", BSpeedDial3);
addAction("/speedDial4", BSpeedDial4);
// Get status
AppServerController.AddAction(MessagePath + "/fullStatus", new Action(SendFullStatus));
// Dial on string
AppServerController.AddAction(MessagePath + "/dial", new Action<string>(s => EISC.SetString(SCurrentDialString, s)));
// Pulse DTMF
AppServerController.AddAction(MessagePath + "/dtmf", new Action<string>(s =>
{
if (DTMFMap.ContainsKey(s))
{
EISC.PulseBool(DTMFMap[s], 100);
}
}));
}
/// <summary>
/// Turns the
/// </summary>
/// <returns></returns>
List<CodecActiveCallItem> GetCurrentCallList()
{
if (CurrentCallItem.Status == eCodecCallStatus.Disconnected)
{
return new List<CodecActiveCallItem>();
}
else
{
return new List<CodecActiveCallItem>() { CurrentCallItem };
}
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Devices.Common.VideoCodec;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Provides a messaging bridge for a VideoCodecBase
/// </summary>
public abstract class MessengerBase
{
/// <summary>
///
/// </summary>
public CotijaSystemController AppServerController { get; private set; }
public string MessagePath { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="codec"></param>
public MessengerBase(string messagePath)
{
if (string.IsNullOrEmpty(messagePath))
throw new ArgumentException("messagePath must not be empty or null");
MessagePath = messagePath;
}
/// <summary>
/// Registers this messenger with appserver controller
/// </summary>
/// <param name="appServerController"></param>
public void RegisterWithAppServer(CotijaSystemController appServerController)
{
if (appServerController == null)
throw new ArgumentNullException("appServerController");
AppServerController = appServerController;
CustomRegisterWithAppServer(AppServerController);
}
/// <summary>
/// Implemented in extending classes. Wire up API calls and feedback here
/// </summary>
/// <param name="appServerController"></param>
abstract protected void CustomRegisterWithAppServer(CotijaSystemController appServerController);
/// <summary>
/// Helper for posting status message
/// </summary>
/// <param name="contentObject">The contents of the content object</param>
protected void PostStatusMessage(object contentObject)
{
AppServerController.SendMessageToServer(JObject.FromObject(new
{
type = MessagePath,
content = contentObject
}));
}
}
}

View File

@@ -15,29 +15,22 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <summary> /// <summary>
/// Provides a messaging bridge for a VideoCodecBase /// Provides a messaging bridge for a VideoCodecBase
/// </summary> /// </summary>
public class VideoCodecBaseMessenger public class VideoCodecBaseMessenger : MessengerBase
{ {
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public VideoCodecBase Codec { get; private set; } public VideoCodecBase Codec { get; private set; }
public CotijaSystemController AppServerController { get; private set; }
public string MessagePath { get; private set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="codec"></param> /// <param name="codec"></param>
public VideoCodecBaseMessenger(VideoCodecBase codec, string messagePath) public VideoCodecBaseMessenger(VideoCodecBase codec, string messagePath) : base(messagePath)
{ {
if (codec == null) if (codec == null)
throw new ArgumentNullException("codec"); throw new ArgumentNullException("codec");
if (string.IsNullOrEmpty(messagePath))
throw new ArgumentException("messagePath must not be empty or null");
MessagePath = messagePath;
Codec = codec; Codec = codec;
codec.CallStatusChange += new EventHandler<CodecCallStatusItemChangeEventArgs>(codec_CallStatusChange); codec.CallStatusChange += new EventHandler<CodecCallStatusItemChangeEventArgs>(codec_CallStatusChange);
codec.IsReadyChange += new EventHandler<EventArgs>(codec_IsReadyChange); codec.IsReadyChange += new EventHandler<EventArgs>(codec_IsReadyChange);
@@ -77,16 +70,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
} }
/// <summary> /// <summary>
/// Registers this codec's messaging with an app server controller /// Called from base's RegisterWithAppServer method
/// </summary> /// </summary>
/// <param name="appServerController"></param> /// <param name="appServerController"></param>
public void RegisterWithAppServer(CotijaSystemController appServerController) protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController)
{ {
if (appServerController == null)
throw new ArgumentNullException("appServerController");
AppServerController = appServerController;
appServerController.AddAction("/device/videoCodec/isReady", new Action(SendIsReady)); appServerController.AddAction("/device/videoCodec/isReady", new Action(SendIsReady));
appServerController.AddAction("/device/videoCodec/fullStatus", new Action(SendVtcFullMessageObject)); appServerController.AddAction("/device/videoCodec/fullStatus", new Action(SendVtcFullMessageObject));
appServerController.AddAction("/device/videoCodec/dial", new Action<string>(s => Codec.Dial(s))); appServerController.AddAction("/device/videoCodec/dial", new Action<string>(s => Codec.Dial(s)));
@@ -242,18 +230,5 @@ namespace PepperDash.Essentials.AppServer.Messengers
hasDirectory = Codec is IHasDirectory hasDirectory = Codec is IHasDirectory
}); });
} }
/// <summary>
/// Helper for posting status message
/// </summary>
/// <param name="contentObject">The contents of the content object</param>
void PostStatusMessage(object contentObject)
{
AppServerController.SendMessageToServer(JObject.FromObject(new
{
type = MessagePath,
content = contentObject
}));
}
} }
} }

View File

@@ -10,6 +10,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Config; using PepperDash.Essentials.Room.Config;
@@ -26,6 +27,14 @@ namespace PepperDash.Essentials.Room.Cotija
/// </summary> /// </summary>
public const uint RoomIsOn = 301; public const uint RoomIsOn = 301;
/// <summary>
/// 41
/// </summary>
public const uint PromptForCode = 41;
/// <summary>
/// 42
/// </summary>
public const uint ClientJoined = 42;
/// <summary> /// <summary>
/// 51 /// 51
/// </summary> /// </summary>
@@ -60,14 +69,15 @@ namespace PepperDash.Essentials.Room.Cotija
/// 63 /// 63
/// </summary> /// </summary>
public const uint ShutdownStart = 63; public const uint ShutdownStart = 63;
/// <summary> /// <summary>
/// 72 /// 72
/// </summary> /// </summary>
public const uint SourceHasChanged = 72; public const uint SourceHasChanged = 72;
/// <summary> /// <summary>
/// 261 - The start of the range of speed dial visibles
/// </summary>
public const uint SpeedDialVisibleStartJoin = 261;
/// <summary>
/// 501 /// 501
/// </summary> /// </summary>
public const uint ConfigIsReady = 501; public const uint ConfigIsReady = 501;
@@ -93,6 +103,16 @@ namespace PepperDash.Essentials.Room.Cotija
/// </summary> /// </summary>
public const uint SelectedSourceKey = 71; public const uint SelectedSourceKey = 71;
/// <summary>
/// 241
/// </summary>
public const uint SpeedDialNameStartJoin = 241;
/// <summary>
/// 251
/// </summary>
public const uint SpeedDialNumberStartJoin = 251;
/// <summary> /// <summary>
/// 501 /// 501
/// </summary> /// </summary>
@@ -145,6 +165,8 @@ namespace PepperDash.Essentials.Room.Cotija
CotijaDdvc01DeviceBridge SourceBridge; CotijaDdvc01DeviceBridge SourceBridge;
Ddvc01AtcMessenger AtcMessenger;
/// <summary> /// <summary>
/// ///
@@ -182,6 +204,9 @@ namespace PepperDash.Essentials.Room.Cotija
SetupFunctions(); SetupFunctions();
SetupFeedbacks(); SetupFeedbacks();
AtcMessenger = new Ddvc01AtcMessenger(EISC, "/device/audioCodec");
AtcMessenger.RegisterWithAppServer(Parent);
EISC.SigChange += EISC_SigChange; EISC.SigChange += EISC_SigChange;
EISC.OnlineStatusChange += (o, a) => EISC.OnlineStatusChange += (o, a) =>
{ {
@@ -216,8 +241,6 @@ namespace PepperDash.Essentials.Room.Cotija
} }
}, "mobilebridgedump", "Dumps DDVC01 bridge EISC data b,u,s", ConsoleAccessLevelEnum.AccessOperator); }, "mobilebridgedump", "Dumps DDVC01 bridge EISC data b,u,s", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => LoadConfigValues(), "loadddvc", "", ConsoleAccessLevelEnum.AccessOperator);
return base.CustomActivate(); return base.CustomActivate();
} }
@@ -227,6 +250,9 @@ namespace PepperDash.Essentials.Room.Cotija
/// </summary> /// </summary>
void SetupFunctions() void SetupFunctions()
{ {
#warning need join numbers for these
Parent.AddAction(@"/room/room1/promptForCode", new Action(() => EISC.PulseBool(BoolJoin.PromptForCode)));
Parent.AddAction(@"/room/room1/clientJoined", new Action(() => EISC.PulseBool(BoolJoin.ClientJoined)));
Parent.AddAction(@"/room/room1/status", new Action(SendFullStatus)); Parent.AddAction(@"/room/room1/status", new Action(SendFullStatus));
@@ -238,6 +264,10 @@ namespace PepperDash.Essentials.Room.Cotija
Parent.AddAction(@"/room/room1/defaultsource", new Action(() => Parent.AddAction(@"/room/room1/defaultsource", new Action(() =>
EISC.PulseBool(BoolJoin.ActivitySharePress))); EISC.PulseBool(BoolJoin.ActivitySharePress)));
Parent.AddAction(@"/room/room1/activityVideo", new Action(() =>
EISC.PulseBool(BoolJoin.ActivityVideoCallPress)));
Parent.AddAction(@"/room/room1/activityPhone", new Action(() =>
EISC.PulseBool(BoolJoin.ActivityPhoneCallPress)));
Parent.AddAction(@"/room/room1/volumes/master/level", new Action<ushort>(u => Parent.AddAction(@"/room/room1/volumes/master/level", new Action<ushort>(u =>
EISC.SetUshort(UshortJoin.MasterVolumeLevel, u))); EISC.SetUshort(UshortJoin.MasterVolumeLevel, u)));
@@ -250,20 +280,26 @@ namespace PepperDash.Essentials.Room.Cotija
EISC.PulseBool(BoolJoin.ShutdownEnd))); EISC.PulseBool(BoolJoin.ShutdownEnd)));
Parent.AddAction(@"/room/room1/shutdownCancel", new Action(() => Parent.AddAction(@"/room/room1/shutdownCancel", new Action(() =>
EISC.PulseBool(BoolJoin.ShutdownCancel))); EISC.PulseBool(BoolJoin.ShutdownCancel)));
// Source Device (Current Source)'
SourceDeviceMapDictionary sourceJoinMap = new SourceDeviceMapDictionary();
var prefix = @"/device/currentSource/";
foreach (var item in sourceJoinMap)
{
Parent.AddAction(string.Format("{0}{1}", prefix, item.Key), new PressAndHoldAction(b => EISC.SetBool(item.Value, b)));
}
} }
/// <summary>
///
/// </summary>
/// <param name="devKey"></param>
void SetupSourceFunctions(string devKey)
{
SourceDeviceMapDictionary sourceJoinMap = new SourceDeviceMapDictionary();
var prefix = string.Format("/device/{0}/", devKey);
foreach (var item in sourceJoinMap)
{
var join = item.Value;
Parent.AddAction(string.Format("{0}{1}", prefix, item.Key), new PressAndHoldAction(b => EISC.SetBool(join, b)));
}
}
/// <summary> /// <summary>
/// Links feedbacks to whatever is gonna happen! /// Links feedbacks to whatever is gonna happen!
/// </summary> /// </summary>
@@ -345,11 +381,11 @@ namespace PepperDash.Essentials.Room.Cotija
var version = Assembly.GetExecutingAssembly().GetName().Version; var version = Assembly.GetExecutingAssembly().GetName().Version;
co.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); co.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
//Room //Room
if (co.Rooms == null) //if (co.Rooms == null)
co.Rooms = new List<DeviceConfig>(); // always start fresh in case simpl changed
var rm = new DeviceConfig(); co.Rooms = new List<EssentialsRoomConfig>();
var rm = new EssentialsRoomConfig();
if (co.Rooms.Count == 0) if (co.Rooms.Count == 0)
{ {
Debug.Console(0, this, "Adding room to config"); Debug.Console(0, this, "Adding room to config");
@@ -388,6 +424,11 @@ namespace PepperDash.Essentials.Room.Cotija
var name = EISC.StringOutput[i + 1].StringValue; var name = EISC.StringOutput[i + 1].StringValue;
rmProps.SpeedDials.Add(new DDVC01SpeedDial { Number = num, Name = name}); rmProps.SpeedDials.Add(new DDVC01SpeedDial { Number = num, Name = name});
} }
// This MAY need a check
rmProps.AudioCodecKey = "audioCodec";
rmProps.VideoCodecKey = null; // "videoCodec";
// volume control names // volume control names
var volCount = EISC.UShortOutput[701].UShortValue; var volCount = EISC.UShortOutput[701].UShortValue;
@@ -403,7 +444,10 @@ namespace PepperDash.Essentials.Room.Cotija
co.Devices = new List<DeviceConfig>(); co.Devices = new List<DeviceConfig>();
// clear out previous DDVC devices // clear out previous DDVC devices
co.Devices.RemoveAll(d => d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase)); co.Devices.RemoveAll(d =>
d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase)
|| d.Key.Equals("audioCodec", StringComparison.OrdinalIgnoreCase)
|| d.Key.Equals("videoCodec", StringComparison.OrdinalIgnoreCase));
rmProps.SourceListKey = "default"; rmProps.SourceListKey = "default";
rm.Properties = JToken.FromObject(rmProps); rm.Properties = JToken.FromObject(rmProps);
@@ -422,6 +466,7 @@ namespace PepperDash.Essentials.Room.Cotija
break; break;
var icon = EISC.StringOutput[651 + i].StringValue; var icon = EISC.StringOutput[651 + i].StringValue;
var key = EISC.StringOutput[671 + i].StringValue; var key = EISC.StringOutput[671 + i].StringValue;
var type = EISC.StringOutput[701 + i].StringValue; var type = EISC.StringOutput[701 + i].StringValue;
Debug.Console(0, this, "Adding source {0} '{1}'", key, name); Debug.Console(0, this, "Adding source {0} '{1}'", key, name);
@@ -430,6 +475,7 @@ namespace PepperDash.Essentials.Room.Cotija
Name = name, Name = name,
Order = (int)i + 1, Order = (int)i + 1,
SourceKey = key, SourceKey = key,
Type = eSourceListItemType.Route
}; };
newSl.Add(key, newSLI); newSl.Add(key, newSLI);
@@ -447,10 +493,50 @@ namespace PepperDash.Essentials.Room.Cotija
Type = type Type = type
}; };
co.Devices.Add(devConf); co.Devices.Add(devConf);
if (group.ToLower().StartsWith("settopbox")) // Add others here as needed
{
SetupSourceFunctions(key);
}
} }
co.SourceLists.Add("default", newSl); co.SourceLists.Add("default", newSl);
// build "audioCodec" config if we need
if (!string.IsNullOrEmpty(rmProps.AudioCodecKey))
{
var acFavs = new List<PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem>();
for (uint i = 0; i < 4; i++)
{
if (!EISC.GetBool(BoolJoin.SpeedDialVisibleStartJoin + i))
{
break;
}
acFavs.Add(new PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem()
{
Name = EISC.GetString(StringJoin.SpeedDialNameStartJoin + i),
Number = EISC.GetString(StringJoin.SpeedDialNumberStartJoin + i),
Type = PepperDash.Essentials.Devices.Common.Codec.eCodecCallType.Audio
});
}
var acProps = new
{
favorites = acFavs
};
var acStr = "audioCodec";
var acConf = new DeviceConfig()
{
Group = acStr,
Key = acStr,
Name = acStr,
Type = acStr,
Properties = JToken.FromObject(acProps)
};
co.Devices.Add(acConf);
}
Debug.Console(0, this, "******* CONFIG FROM DDVC: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented)); Debug.Console(0, this, "******* CONFIG FROM DDVC: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented));
var handler = ConfigurationIsReady; var handler = ConfigurationIsReady;
@@ -462,6 +548,9 @@ namespace PepperDash.Essentials.Room.Cotija
ConfigIsLoaded = true; ConfigIsLoaded = true;
} }
/// <summary>
///
/// </summary>
void SendFullStatus() void SendFullStatus()
{ {
if (ConfigIsLoaded) if (ConfigIsLoaded)
@@ -585,86 +674,5 @@ namespace PepperDash.Essentials.Room.Cotija
EISC.StringInput[StringJoin.UserCodeToSystem].StringValue = UserCode; EISC.StringInput[StringJoin.UserCodeToSystem].StringValue = UserCode;
EISC.StringInput[StringJoin.ServerUrl].StringValue = Parent.Config.ClientAppUrl; EISC.StringInput[StringJoin.ServerUrl].StringValue = Parent.Config.ClientAppUrl;
} }
/// <summary>
///
/// </summary>
/// <param name="oldKey"></param>
/// <param name="newKey"></param>
void SourceChange(string oldKey, string newKey)
{
/* Example message
* {
"type":"/room/status",
"content": {
"selectedSourceKey": "off",
}
}
*/
//if (type == ChangeType.WillChange)
//{
// // Disconnect from previous source
// if (info != null)
// {
// var previousDev = info.SourceDevice;
// // device type interfaces
// if (previousDev is ISetTopBoxControls)
// (previousDev as ISetTopBoxControls).UnlinkActions(Parent);
// // common interfaces
// if (previousDev is IChannel)
// (previousDev as IChannel).UnlinkActions(Parent);
// if (previousDev is IColor)
// (previousDev as IColor).UnlinkActions(Parent);
// if (previousDev is IDPad)
// (previousDev as IDPad).UnlinkActions(Parent);
// if (previousDev is IDvr)
// (previousDev as IDvr).UnlinkActions(Parent);
// if (previousDev is INumericKeypad)
// (previousDev as INumericKeypad).UnlinkActions(Parent);
// if (previousDev is IPower)
// (previousDev as IPower).UnlinkActions(Parent);
// if (previousDev is ITransport)
// (previousDev as ITransport).UnlinkActions(Parent);
// }
// var huddleRoom = room as EssentialsHuddleSpaceRoom;
// JObject roomStatus = new JObject();
// roomStatus.Add("selectedSourceKey", huddleRoom.CurrentSourceInfoKey);
// JObject message = new JObject();
// message.Add("type", "/room/status/");
// message.Add("content", roomStatus);
// Parent.PostToServer(message);
//}
//else
//{
// if (info != null)
// {
// var dev = info.SourceDevice;
// if (dev is ISetTopBoxControls)
// (dev as ISetTopBoxControls).LinkActions(Parent);
// if (dev is IChannel)
// (dev as IChannel).LinkActions(Parent);
// if (dev is IColor)
// (dev as IColor).LinkActions(Parent);
// if (dev is IDPad)
// (dev as IDPad).LinkActions(Parent);
// if (dev is IDvr)
// (dev as IDvr).LinkActions(Parent);
// if (dev is INumericKeypad)
// (dev as INumericKeypad).LinkActions(Parent);
// if (dev is IPower)
// (dev as IPower).LinkActions(Parent);
// if (dev is ITransport)
// (dev as ITransport).LinkActions(Parent);
// }
//}
}
} }
} }

View File

@@ -74,7 +74,7 @@ namespace PepperDash.Essentials
{ {
Parent.AddAction(string.Format(@"/room/{0}/volumes/master/level", Room.Key), new Action<ushort>(u => Parent.AddAction(string.Format(@"/room/{0}/volumes/master/level", Room.Key), new Action<ushort>(u =>
(volumeRoom.CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(u))); (volumeRoom.CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(u)));
Parent.AddAction(string.Format(@"/room/{0}/volumes/master/mute", Room.Key), new Action(() => Parent.AddAction(string.Format(@"/room/{0}/volumes/master/muteToggle", Room.Key), new Action(() =>
volumeRoom.CurrentVolumeControls.MuteToggle())); volumeRoom.CurrentVolumeControls.MuteToggle()));
volumeRoom.CurrentVolumeDeviceChange += new EventHandler<VolumeDeviceChangeEventArgs>(Room_CurrentVolumeDeviceChange); volumeRoom.CurrentVolumeDeviceChange += new EventHandler<VolumeDeviceChangeEventArgs>(Room_CurrentVolumeDeviceChange);
@@ -82,7 +82,7 @@ namespace PepperDash.Essentials
var currentVolumeDevice = volumeRoom.CurrentVolumeControls as IBasicVolumeWithFeedback; var currentVolumeDevice = volumeRoom.CurrentVolumeControls as IBasicVolumeWithFeedback;
if (currentVolumeDevice != null) if (currentVolumeDevice != null)
{ {
currentVolumeDevice.MuteFeedback.OutputChange += VolumeLevelFeedback_OutputChange; currentVolumeDevice.MuteFeedback.OutputChange += MuteFeedback_OutputChange;
currentVolumeDevice.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange; currentVolumeDevice.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
} }
} }
@@ -471,7 +471,6 @@ namespace PepperDash.Essentials
} }
return vtc; return vtc;
} }
} }
/// <summary> /// <summary>

View File

@@ -42,6 +42,10 @@ namespace PepperDash.Essentials
// ConsoleAccessLevelEnum.AccessOperator); // ConsoleAccessLevelEnum.AccessOperator);
// CrestronConsole.AddNewConsoleCommand(S => { ConfigWriter.WriteConfigFile(null); }, "writeconfig", "writes the current config to a file", ConsoleAccessLevelEnum.AccessOperator); // CrestronConsole.AddNewConsoleCommand(S => { ConfigWriter.WriteConfigFile(null); }, "writeconfig", "writes the current config to a file", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "CONSOLE MESSAGE: {0}", s);
}, "appdebugmessage", "Writes message to log", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.AddNewConsoleCommand(s =>
{ {

View File

@@ -1,367 +0,0 @@
using System;
using System.Linq;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.CrestronThread;
using PepperDash.Core;
using PepperDash.Core.PortalSync;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common;
using PepperDash.Essentials.DM;
using PepperDash.Essentials.Fusion;
using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials
{
public class ControlSystem : CrestronControlSystem
{
PepperDashPortalSyncClient PortalSync;
HttpLogoServer LogoServer;
public ControlSystem()
: base()
{
Thread.MaxNumberOfUserThreads = 400;
Global.ControlSystem = this;
DeviceManager.Initialize(this);
}
/// <summary>
/// Git 'er goin'
/// </summary>
public override void InitializeSystem()
{
<<<<<<< HEAD
DeterminePlatform();
//CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file",
// ConsoleAccessLevelEnum.AccessOperator);
=======
CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file",
ConsoleAccessLevelEnum.AccessOperator);
>>>>>>> 600b9f11ff1bbc186f7c2a2945955731b3523b3c
CrestronConsole.AddNewConsoleCommand(s =>
{
foreach (var tl in TieLineCollection.Default)
CrestronConsole.ConsoleCommandResponse(" {0}\r", tl);
},
"listtielines", "Prints out all tie lines", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
CrestronConsole.ConsoleCommandResponse
("Current running configuration. This is the merged system and template configuration");
CrestronConsole.ConsoleCommandResponse(Newtonsoft.Json.JsonConvert.SerializeObject
(ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented));
}, "showconfig", "Shows the current running merged config", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
CrestronConsole.ConsoleCommandResponse("This system can be found at the following URLs:\r" +
"System URL: {0}\r" +
"Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl);
}, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator);
//GoWithLoad();
}
/// <summary>
/// Determines if the program is running on a processor (appliance) or server (XiO Edge).
///
/// Sets Global.FilePathPrefix based on platform
/// </summary>
public void DeterminePlatform()
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Determining Platform....");
string filePathPrefix;
var dirSeparator = Global.DirectorySeparator;
var version = Crestron.SimplSharp.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
var versionString = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
string directoryPrefix;
//directoryPrefix = Crestron.SimplSharp.CrestronIO.Directory.GetApplicationRootDirectory();
#warning ^ For use with beta Include4.dat for XiO Edge
directoryPrefix = "";
if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server)
{
filePathPrefix = directoryPrefix + dirSeparator + "NVRAM"
+ dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on 3-series Appliance", versionString);
}
else
{
filePathPrefix = directoryPrefix + dirSeparator + "User" + dirSeparator;
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on XiO Edge Server", versionString);
}
Global.SetFilePathPrefix(filePathPrefix);
}
/// <summary>
/// Do it, yo
/// </summary>
public void GoWithLoad()
{
try
{
CrestronConsole.AddNewConsoleCommand(EnablePortalSync, "portalsync", "Loads Portal Sync",
ConsoleAccessLevelEnum.AccessOperator);
//PortalSync = new PepperDashPortalSyncClient();
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials load from configuration");
var filesReady = SetupFilesystem();
if (filesReady)
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Folder structure verified. Loading config...");
if (!ConfigReader.LoadConfig2())
return;
Load();
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Essentials load complete\r" +
"-------------------------------------------------------------");
}
else
{
Debug.Console(0,
"------------------------------------------------\r" +
"------------------------------------------------\r" +
"------------------------------------------------\r" +
"Essentials file structure setup completed.\r" +
"Please load config, sgd and ir files and\r" +
"restart program.\r" +
"------------------------------------------------\r" +
"------------------------------------------------\r" +
"------------------------------------------------");
}
}
catch (Exception e)
{
Debug.Console(0, "FATAL INITIALIZE ERROR. System is in an inconsistent state:\r{0}", e);
}
}
/// <summary>
/// Verifies filesystem is set up. IR, SGD, and program1 folders
/// </summary>
bool SetupFilesystem()
{
Debug.Console(0, "Verifying and/or creating folder structure");
var configDir = Global.FilePathPrefix;
var configExists = Directory.Exists(configDir);
if (!configExists)
Directory.Create(configDir);
var irDir = Global.FilePathPrefix + "ir";
if (!Directory.Exists(irDir))
Directory.Create(irDir);
var sgdDir = Global.FilePathPrefix + "sgd";
if (!Directory.Exists(sgdDir))
Directory.Create(sgdDir);
return configExists;
}
public void EnablePortalSync(string s)
{
if (s.ToLower() == "enable")
{
CrestronConsole.ConsoleCommandResponse("Portal Sync features enabled");
PortalSync = new PepperDashPortalSyncClient();
}
}
public void TearDown()
{
Debug.Console(0, "Tearing down existing system");
DeviceManager.DeactivateAll();
TieLineCollection.Default.Clear();
foreach (var key in DeviceManager.GetDevices())
DeviceManager.RemoveDevice(key);
Debug.Console(0, "Tear down COMPLETE");
}
/// <summary>
///
/// </summary>
void Load()
{
LoadDevices();
LoadTieLines();
LoadRooms();
LoadLogoServer();
DeviceManager.ActivateAll();
}
/// <summary>
/// Reads all devices from config and adds them to DeviceManager
/// </summary>
public void LoadDevices()
{
foreach (var devConf in ConfigReader.ConfigObject.Devices)
{
try
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Creating device '{0}'", devConf.Key);
// Skip this to prevent unnecessary warnings
if (devConf.Key == "processor")
continue;
// Try local factory first
var newDev = DeviceFactory.GetDevice(devConf);
// Then associated library factories
if (newDev == null)
newDev = PepperDash.Essentials.Devices.Common.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.DM.DeviceFactory.GetDevice(devConf);
if (newDev == null)
newDev = PepperDash.Essentials.Devices.Displays.DisplayDeviceFactory.GetDevice(devConf);
if (newDev != null)
DeviceManager.AddDevice(newDev);
else
Debug.Console(0, Debug.ErrorLogLevel.Notice, "ERROR: Cannot load unknown device type '{0}', key '{1}'.", devConf.Type, devConf.Key);
}
catch (Exception e)
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "ERROR: Creating device {0}. Skipping device. \r{1}", devConf.Key, e);
}
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Devices Loaded.");
}
/// <summary>
/// Helper method to load tie lines. This should run after devices have loaded
/// </summary>
public void LoadTieLines()
{
// In the future, we can't necessarily just clear here because devices
// might be making their own internal sources/tie lines
var tlc = TieLineCollection.Default;
//tlc.Clear();
if (ConfigReader.ConfigObject.TieLines == null)
{
return;
}
foreach (var tieLineConfig in ConfigReader.ConfigObject.TieLines)
{
var newTL = tieLineConfig.GetTieLine();
if (newTL != null)
tlc.Add(newTL);
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Tie Lines Loaded.");
}
/// <summary>
/// Reads all rooms from config and adds them to DeviceManager
/// </summary>
public void LoadRooms()
{
if (ConfigReader.ConfigObject.Rooms == null)
{
Debug.Console(0, Debug.ErrorLogLevel.Warning, "WARNING: Configuration contains no rooms");
return;
}
foreach (var roomConfig in ConfigReader.ConfigObject.Rooms)
{
var room = roomConfig.GetRoomObject();
if (room != null)
{
if (room is EssentialsHuddleSpaceRoom)
{
DeviceManager.AddDevice(room);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion");
DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemControllerBase((EssentialsHuddleSpaceRoom)room, 0xf1));
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Cotija Bridge...");
// Cotija bridge
var bridge = new CotijaEssentialsHuddleSpaceRoomBridge(room as EssentialsHuddleSpaceRoom);
AddBridgePostActivationHelper(bridge); // Lets things happen later when all devices are present
DeviceManager.AddDevice(bridge);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Cotija Bridge Added...");
}
else if (room is EssentialsHuddleVtc1Room)
{
DeviceManager.AddDevice(room);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion");
DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, 0xf1));
}
else
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is NOT EssentialsHuddleSpaceRoom, attempting to add to DeviceManager w/o Fusion");
DeviceManager.AddDevice(room);
}
}
else
Debug.Console(0, Debug.ErrorLogLevel.Notice, "WARNING: Cannot create room from config, key '{0}'", roomConfig.Key);
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Rooms Loaded.");
}
/// <summary>
/// Helps add the post activation steps that link bridges to main controller
/// </summary>
/// <param name="bridge"></param>
void AddBridgePostActivationHelper(CotijaBridgeBase bridge)
{
bridge.AddPostActivationAction(() =>
{
var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController;
if (parent == null)
{
Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present");
}
Debug.Console(0, bridge, "Linking to parent controller");
bridge.AddParent(parent);
parent.AddBridge(bridge);
});
}
/// <summary>
/// Fires up a logo server if not already running
/// </summary>
void LoadLogoServer()
{
try
{
LogoServer = new HttpLogoServer(8080, Global.FilePathPrefix + "html" + Global.DirectorySeparator + "logo");
}
catch (Exception)
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "NOTICE: Logo server cannot be started. Likely already running in another program");
}
}
}
}

View File

@@ -108,6 +108,8 @@
<Reference Include="System.Data" /> <Reference Include="System.Data" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AppServer\Messengers\AtcDdvc01Messenger.cs" />
<Compile Include="AppServer\Messengers\MessengerBase.cs" />
<Compile Include="AppServer\Messengers\VideoCodecBaseMessenger.cs" /> <Compile Include="AppServer\Messengers\VideoCodecBaseMessenger.cs" />
<Compile Include="Audio\EssentialsVolumeLevelConfig.cs" /> <Compile Include="Audio\EssentialsVolumeLevelConfig.cs" />
<Compile Include="Bridges\BridgeBase.cs" /> <Compile Include="Bridges\BridgeBase.cs" />

View File

@@ -20,5 +20,7 @@ namespace PepperDash.Essentials.Room.Config
public string DefaultSourceItem { get; set; } public string DefaultSourceItem { get; set; }
[JsonProperty("videoCodecKey")] [JsonProperty("videoCodecKey")]
public string VideoCodecKey { get; set; } public string VideoCodecKey { get; set; }
[JsonProperty("audioCodecKey")]
public string AudioCodecKey { get; set; }
} }
} }