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.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Cotija;
namespace PepperDash.Essentials
@@ -24,6 +23,8 @@ namespace PepperDash.Essentials
{
WebSocketClient WSClient;
//bool LinkUp;
/// <summary>
/// Prevents post operations from stomping on each other and getting lost
/// </summary>
@@ -83,12 +84,33 @@ namespace PepperDash.Essentials
CrestronConsole.AddNewConsoleCommand(TestHttpRequest,
"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.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>
/// Sends message to server to indicate the system is shutting down
/// </summary>
@@ -97,11 +119,7 @@ namespace PepperDash.Essentials
{
if (programEventType == eProgramStatusEventType.Stopping && WSClient.Connected)
{
SendMessageToServer(JObject.FromObject( new
{
type = "/system/close"
}));
CleanUpWebsocketClient();
}
}
@@ -158,6 +176,7 @@ namespace PepperDash.Essentials
else
{
Debug.Console(0, this, "Adding room bridge and sending configuration");
SystemUuid = ConfigReader.ConfigObject.SystemUuid;
RegisterSystemToServer();
}
}
@@ -170,6 +189,7 @@ namespace PepperDash.Essentials
void bridge_ConfigurationIsReady(object sender, EventArgs e)
{
Debug.Console(1, this, "Bridge ready. Registering");
SystemUuid = ConfigReader.ConfigObject.SystemUuid;
// send the configuration object to the server
RegisterSystemToServer();
}
@@ -189,6 +209,8 @@ namespace PepperDash.Essentials
/// <param name="command"></param>
void AuthorizeSystem(string code)
{
SystemUuid = ConfigReader.ConfigObject.SystemUuid;
if (string.IsNullOrEmpty(SystemUuid))
{
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)
{
Debug.Console(0, "System authorized, sending config.");
#warning This registration may need to wait for config ready. Maybe.
RegisterSystemToServer();
}
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>
void RegisterSystemToServer()
{
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();
}
ConnectWebsocketClient();
}
/// <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>
/// Sends a message to the server from a room
/// </summary>
@@ -340,78 +409,67 @@ namespace PepperDash.Essentials
/// <param name="o">object to be serialized and sent in post body</param>
public void SendMessageToServer(JObject o)
{
if (WSClient != null && WSClient.Connected)
{
string message = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
Debug.Console(1, this, "Message TX: {0}", 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);
//WSClient.SendAsync(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);
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>
/// Disconnects the SSE Client and stops the heartbeat timer
/// </summary>
/// <param name="command"></param>
void DisconnectStreamClient(string command)
void CleanUpWebsocketClient()
{
//if(SseClient != null)
// SseClient.Disconnect();
if (WSClient != null && WSClient.Connected)
WSClient.Disconnect();
if (ServerHeartbeatCheckTimer != null)
{
ServerHeartbeatCheckTimer.Stop();
ServerHeartbeatCheckTimer = null;
}
Debug.Console(1, this, "Disconnecting websocket");
if (WSClient != null)
{
WSClient.SendCallBack = null;
WSClient.ReceiveCallBack = null;
WSClient.ConnectionCallBack = null;
WSClient.DisconnectCallBack = null;
if (WSClient.Connected)
{
WSClient.Disconnect();
}
WSClient = null;
}
}
/// <summary>
/// The callback that fires when we get a response from our registration attempt
/// </summary>
/// <param name="resp"></param>
/// <param name="err"></param>
void RegistrationConnectionCallback(HttpClientResponse resp, HTTP_CALLBACK_ERROR err)
{
CheckHttpDebug(resp, err);
Debug.Console(1, this, "RegistrationConnectionCallback: {0}", err);
try
{
if (resp != null && resp.Code == 200)
{
if(ServerReconnectTimer != null)
{
ServerReconnectTimer.Stop();
ServerReconnectTimer = null;
}
// Success here!
ConnectStreamClient();
}
else
{
if (resp != null)
Debug.Console(1, this, "Response from server: {0}\n{1}", resp.Code, err);
else
{
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>
/// <param name="dueTime"></param>
/// <param name="repeatTime"></param>
void StartServerReconnectTimer()
{
StopServerReconnectTimer();
ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, ServerReconnectInterval);
Debug.Console(1, this, "Reconnect Timer Started.");
}
/// <summary>
/// Does what it says
/// </summary>
void StopServerReconnectTimer()
{
if (ServerReconnectTimer != null)
{
ServerReconnectTimer.Stop();
ServerReconnectTimer = null;
}
}
/// <summary>
/// Executes when we don't get a heartbeat message in time. Triggers reconnect.
@@ -425,23 +483,8 @@ namespace PepperDash.Essentials
ServerHeartbeatCheckTimer.Stop();
ServerHeartbeatCheckTimer = null;
}
StartReconnectTimer();
}
/// <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);
CleanUpWebsocketClient();
StartServerReconnectTimer();
}
/// <summary>
@@ -451,37 +494,42 @@ namespace PepperDash.Essentials
/// <param name="repeatTime"></param>
void ResetOrStartHearbeatTimer()
{
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)
if (ServerHeartbeatCheckTimer == 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>
/// Resets reconnect timer and updates usercode
/// </summary>
@@ -514,12 +562,26 @@ namespace PepperDash.Essentials
{
if (HttpDebugEnabled)
{
Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------");
Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl.ToString());
Debug.Console(0, this, "HTTP Response 'error' {0}", e);
Debug.Console(0, this, "HTTP Response code: {0}", r.Code);
Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString);
Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------");
try
{
Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------");
if (r != null)
{
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="opcode"></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)
{
var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length);
if(rx.Length > 0)
ParseStreamRx(rx);
WSClient.ReceiveAsync();
return 1;
if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME)
{
var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length);
if (rx.Length > 0)
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>
@@ -545,11 +621,11 @@ namespace PepperDash.Essentials
/// </summary>
/// <param name="result"></param>
/// <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);
return 1;
if(result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS)
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SendCallback questionable result: {0}", result);
return 1;
}
/// <summary>
@@ -562,7 +638,7 @@ namespace PepperDash.Essentials
if(string.IsNullOrEmpty(message))
return;
Debug.Console(1, this, "Message RX: '{0}'", message);
Debug.Console(1, this, "Message RX: {0}", message);
try
{
var messageObj = JObject.Parse(message);
@@ -571,6 +647,7 @@ namespace PepperDash.Essentials
if (type == "hello")
{
SendInitialMessage();
ResetOrStartHearbeatTimer();
}
else if (type == "/system/heartbeat")
@@ -579,11 +656,11 @@ namespace PepperDash.Essentials
}
else if (type == "close")
{
WSClient.Disconnect();
Debug.Console(1, this, "Received close message from server.");
// DisconnectWebsocketClient();
ServerHeartbeatCheckTimer.Stop();
// Start the reconnect timer
StartReconnectTimer();
if (ServerHeartbeatCheckTimer != null)
ServerHeartbeatCheckTimer.Stop();
}
else
{
@@ -620,7 +697,7 @@ namespace PepperDash.Essentials
}
case "held":
{
if (!PushedActions.ContainsKey(type))
if (PushedActions.ContainsKey(type))
{
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>
/// Provides a messaging bridge for a VideoCodecBase
/// </summary>
public class VideoCodecBaseMessenger
public class VideoCodecBaseMessenger : MessengerBase
{
/// <summary>
///
/// </summary>
public VideoCodecBase Codec { get; private set; }
public CotijaSystemController AppServerController { get; private set; }
public string MessagePath { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="codec"></param>
public VideoCodecBaseMessenger(VideoCodecBase codec, string messagePath)
public VideoCodecBaseMessenger(VideoCodecBase codec, string messagePath) : base(messagePath)
{
if (codec == null)
throw new ArgumentNullException("codec");
if (string.IsNullOrEmpty(messagePath))
throw new ArgumentException("messagePath must not be empty or null");
MessagePath = messagePath;
Codec = codec;
codec.CallStatusChange += new EventHandler<CodecCallStatusItemChangeEventArgs>(codec_CallStatusChange);
codec.IsReadyChange += new EventHandler<EventArgs>(codec_IsReadyChange);
@@ -77,16 +70,11 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
/// <summary>
/// Registers this codec's messaging with an app server controller
/// Called from base's RegisterWithAppServer method
/// </summary>
/// <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/fullStatus", new Action(SendVtcFullMessageObject));
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
});
}
/// <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 PepperDash.Core;
using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Config;
@@ -26,6 +27,14 @@ namespace PepperDash.Essentials.Room.Cotija
/// </summary>
public const uint RoomIsOn = 301;
/// <summary>
/// 41
/// </summary>
public const uint PromptForCode = 41;
/// <summary>
/// 42
/// </summary>
public const uint ClientJoined = 42;
/// <summary>
/// 51
/// </summary>
@@ -60,14 +69,15 @@ namespace PepperDash.Essentials.Room.Cotija
/// 63
/// </summary>
public const uint ShutdownStart = 63;
/// <summary>
/// 72
/// </summary>
public const uint SourceHasChanged = 72;
/// <summary>
/// 261 - The start of the range of speed dial visibles
/// </summary>
public const uint SpeedDialVisibleStartJoin = 261;
/// <summary>
/// 501
/// </summary>
public const uint ConfigIsReady = 501;
@@ -92,6 +102,16 @@ namespace PepperDash.Essentials.Room.Cotija
/// 71
/// </summary>
public const uint SelectedSourceKey = 71;
/// <summary>
/// 241
/// </summary>
public const uint SpeedDialNameStartJoin = 241;
/// <summary>
/// 251
/// </summary>
public const uint SpeedDialNumberStartJoin = 251;
/// <summary>
/// 501
@@ -145,6 +165,8 @@ namespace PepperDash.Essentials.Room.Cotija
CotijaDdvc01DeviceBridge SourceBridge;
Ddvc01AtcMessenger AtcMessenger;
/// <summary>
///
@@ -182,6 +204,9 @@ namespace PepperDash.Essentials.Room.Cotija
SetupFunctions();
SetupFeedbacks();
AtcMessenger = new Ddvc01AtcMessenger(EISC, "/device/audioCodec");
AtcMessenger.RegisterWithAppServer(Parent);
EISC.SigChange += EISC_SigChange;
EISC.OnlineStatusChange += (o, a) =>
{
@@ -216,8 +241,6 @@ namespace PepperDash.Essentials.Room.Cotija
}
}, "mobilebridgedump", "Dumps DDVC01 bridge EISC data b,u,s", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => LoadConfigValues(), "loadddvc", "", ConsoleAccessLevelEnum.AccessOperator);
return base.CustomActivate();
}
@@ -227,6 +250,9 @@ namespace PepperDash.Essentials.Room.Cotija
/// </summary>
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));
@@ -238,6 +264,10 @@ namespace PepperDash.Essentials.Room.Cotija
Parent.AddAction(@"/room/room1/defaultsource", new Action(() =>
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 =>
EISC.SetUshort(UshortJoin.MasterVolumeLevel, u)));
@@ -250,20 +280,26 @@ namespace PepperDash.Essentials.Room.Cotija
EISC.PulseBool(BoolJoin.ShutdownEnd)));
Parent.AddAction(@"/room/room1/shutdownCancel", new Action(() =>
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>
/// Links feedbacks to whatever is gonna happen!
/// </summary>
@@ -345,11 +381,11 @@ namespace PepperDash.Essentials.Room.Cotija
var version = Assembly.GetExecutingAssembly().GetName().Version;
co.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);
//Room
if (co.Rooms == null)
co.Rooms = new List<DeviceConfig>();
var rm = new DeviceConfig();
//if (co.Rooms == null)
// always start fresh in case simpl changed
co.Rooms = new List<EssentialsRoomConfig>();
var rm = new EssentialsRoomConfig();
if (co.Rooms.Count == 0)
{
Debug.Console(0, this, "Adding room to config");
@@ -388,6 +424,11 @@ namespace PepperDash.Essentials.Room.Cotija
var name = EISC.StringOutput[i + 1].StringValue;
rmProps.SpeedDials.Add(new DDVC01SpeedDial { Number = num, Name = name});
}
// This MAY need a check
rmProps.AudioCodecKey = "audioCodec";
rmProps.VideoCodecKey = null; // "videoCodec";
// volume control names
var volCount = EISC.UShortOutput[701].UShortValue;
@@ -403,7 +444,10 @@ namespace PepperDash.Essentials.Room.Cotija
co.Devices = new List<DeviceConfig>();
// 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";
rm.Properties = JToken.FromObject(rmProps);
@@ -422,6 +466,7 @@ namespace PepperDash.Essentials.Room.Cotija
break;
var icon = EISC.StringOutput[651 + i].StringValue;
var key = EISC.StringOutput[671 + i].StringValue;
var type = EISC.StringOutput[701 + i].StringValue;
Debug.Console(0, this, "Adding source {0} '{1}'", key, name);
@@ -430,6 +475,7 @@ namespace PepperDash.Essentials.Room.Cotija
Name = name,
Order = (int)i + 1,
SourceKey = key,
Type = eSourceListItemType.Route
};
newSl.Add(key, newSLI);
@@ -447,10 +493,50 @@ namespace PepperDash.Essentials.Room.Cotija
Type = type
};
co.Devices.Add(devConf);
if (group.ToLower().StartsWith("settopbox")) // Add others here as needed
{
SetupSourceFunctions(key);
}
}
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));
var handler = ConfigurationIsReady;
@@ -462,6 +548,9 @@ namespace PepperDash.Essentials.Room.Cotija
ConfigIsLoaded = true;
}
/// <summary>
///
/// </summary>
void SendFullStatus()
{
if (ConfigIsLoaded)
@@ -585,86 +674,5 @@ namespace PepperDash.Essentials.Room.Cotija
EISC.StringInput[StringJoin.UserCodeToSystem].StringValue = UserCode;
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 =>
(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.CurrentVolumeDeviceChange += new EventHandler<VolumeDeviceChangeEventArgs>(Room_CurrentVolumeDeviceChange);
@@ -82,7 +82,7 @@ namespace PepperDash.Essentials
var currentVolumeDevice = volumeRoom.CurrentVolumeControls as IBasicVolumeWithFeedback;
if (currentVolumeDevice != null)
{
currentVolumeDevice.MuteFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
currentVolumeDevice.MuteFeedback.OutputChange += MuteFeedback_OutputChange;
currentVolumeDevice.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange;
}
}
@@ -470,8 +470,7 @@ namespace PepperDash.Essentials
};
}
return vtc;
}
}
}
/// <summary>