Added Extend Meeting and End Meeting early features, as well as Fusion Events. ECS-316 ECS-317 ECS-320

This commit is contained in:
Neil Dorin
2017-05-17 23:01:29 -06:00
parent 34144c6f13
commit 520f7fa639
7 changed files with 246 additions and 54 deletions

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Fusion
{
public class ScheduleChangeEventArgs : EventArgs
{
public RoomSchedule MyProperty { get; set; }
}
public class MeetingChangeEventArgs : EventArgs
{
public Event Meeting { get; set; }
}
}

View File

@@ -26,6 +26,10 @@ namespace PepperDash.Essentials.Fusion
{ {
public class EssentialsHuddleSpaceFusionSystemController : Device public class EssentialsHuddleSpaceFusionSystemController : Device
{ {
public event EventHandler<ScheduleChangeEventArgs> ScheduleChange;
public event EventHandler<MeetingChangeEventArgs> MeetingEndWarning;
public event EventHandler<MeetingChangeEventArgs> NextMeetingBeginWarning;
FusionRoom FusionRoom; FusionRoom FusionRoom;
EssentialsHuddleSpaceRoom Room; EssentialsHuddleSpaceRoom Room;
Dictionary<Device, BoolInputSig> SourceToFeedbackSigs = Dictionary<Device, BoolInputSig> SourceToFeedbackSigs =
@@ -35,7 +39,7 @@ namespace PepperDash.Essentials.Fusion
StringSigData SourceNameSig; StringSigData SourceNameSig;
ScheduleResponse CurrentSchedule; RoomSchedule CurrentSchedule;
Event NextMeeting; Event NextMeeting;
@@ -49,8 +53,14 @@ namespace PepperDash.Essentials.Fusion
bool IsRegisteredForSchedulePushNotifications = false; bool IsRegisteredForSchedulePushNotifications = false;
CTimer PollTimer = null;
CTimer PushNotificationTimer = null;
// Default poll time is 5 min unless overridden by config value // Default poll time is 5 min unless overridden by config value
int SchedulePollInterval = 300000; public long SchedulePollInterval = 300000;
public long PushNotificationTimeout = 5000;
List<StaticAsset> StaticAssets; List<StaticAsset> StaticAssets;
@@ -65,6 +75,7 @@ namespace PepperDash.Essentials.Fusion
StaticAssets = new List<StaticAsset>(); StaticAssets = new List<StaticAsset>();
var mac = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); var mac = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
var slot = Global.ControlSystem.ProgramNumber; var slot = Global.ControlSystem.ProgramNumber;
@@ -209,7 +220,7 @@ namespace PepperDash.Essentials.Fusion
Debug.Console(0, this, "Fusion Guids successfully read from file:"); Debug.Console(0, this, "Fusion Guids successfully read from file:");
Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1}\n RoomGuid: {2}", Room.Name, IpId, RoomGuid); Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, IpId, RoomGuid);
foreach (StaticAsset asset in StaticAssets) foreach (StaticAsset asset in StaticAssets)
{ {
@@ -228,7 +239,6 @@ namespace PepperDash.Essentials.Fusion
} }
void CreateSymbolAndBasicSigs(uint ipId) void CreateSymbolAndBasicSigs(uint ipId)
{ {
Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
@@ -247,6 +257,7 @@ namespace PepperDash.Essentials.Fusion
CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator);
// Room to fusion room // Room to fusion room
Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig);
@@ -308,12 +319,11 @@ namespace PepperDash.Essentials.Fusion
} }
/// <summary> /// <summary>
/// Generates a room schedule request for this room for the next 24 hours. /// Generates a room schedule request for this room for the next 24 hours.
/// </summary> /// </summary>
/// <param name="requestID">string identifying this request. Used with a corresponding ScheduleResponse value</param> /// <param name="requestID">string identifying this request. Used with a corresponding ScheduleResponse value</param>
public void RequestFullRoomSchedule(string requestID) public void RequestFullRoomSchedule(object callbackObject)
{ {
DateTime now = DateTime.Today; DateTime now = DateTime.Today;
@@ -329,13 +339,20 @@ namespace PepperDash.Essentials.Fusion
// string.Format("<RequestSchedule><RequestID>{0}</RequestID><RoomID>{1}</RoomID><Start>2017-05-02T00:00:00</Start><HourSpan>24</HourSpan></RequestSchedule>", requestID, GUID); // string.Format("<RequestSchedule><RequestID>{0}</RequestID><RoomID>{1}</RoomID><Start>2017-05-02T00:00:00</Start><HourSpan>24</HourSpan></RequestSchedule>", requestID, GUID);
string requestTest = string requestTest =
string.Format("<RequestSchedule><RequestID>{0}</RequestID><RoomID>{1}</RoomID><Start>{2}</Start><HourSpan>24</HourSpan></RequestSchedule>", requestID, RoomGuid, currentTime); string.Format("<RequestSchedule><RequestID>FullSchedleRequest</RequestID><RoomID>{0}</RoomID><Start>{1}</Start><HourSpan>24</HourSpan></RequestSchedule>", RoomGuid, currentTime);
Debug.Console(1, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest); Debug.Console(1, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest);
FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest; FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest;
if (IsRegisteredForSchedulePushNotifications)
PushNotificationTimer.Stop();
} }
/// <summary>
/// Wrapper method to allow console commands to modify the current meeting end time
/// </summary>
/// <param name="command">meetingID extendTime</param>
public void ModifyMeetingEndTimeConsoleHelper(string command) public void ModifyMeetingEndTimeConsoleHelper(string command)
{ {
string requestID; string requestID;
@@ -357,36 +374,99 @@ namespace PepperDash.Essentials.Fusion
Debug.Console(1, this, "Error parsing console command: {0}", e); Debug.Console(1, this, "Error parsing console command: {0}", e);
} }
Event tempEvent = CurrentSchedule.Events.FirstOrDefault(e => e.MeetingID.Equals(meetingID)); ModifyMeetingEndTime(requestID, extendMinutes);
if (tempEvent != null && extendMinutes > -1)
{
ModifyMeetingEndTime(requestID, tempEvent, extendMinutes);
}
else
{
Debug.Console(1, this, "No matching MeetingID found in CurrentSchedule.Events or invalid time specified");
}
} }
/// <summary> /// <summary>
/// Ends or Extends a meeting by the specified number of minutes. /// Ends or Extends the current meeting by the specified number of minutes.
/// </summary> /// </summary>
/// <param name="extendMinutes">Number of minutes to extend the meeting. A value of 0 will end the meeting.</param> /// <param name="extendMinutes">Number of minutes to extend the meeting. A value of 0 will end the meeting.</param>
public void ModifyMeetingEndTime(string requestID, PepperDash.Essentials.Fusion.Event meeting, int extendMinutes) public void ModifyMeetingEndTime(string requestID, int extendMinutes)
{ {
string requestTest = string.Format( if(CurrentMeeting == null)
"<RequestAction><RequestID>{0}</RequestID><RoomID>{1}</RoomID><ActionID>MeetingChange</ActionID><Parameters><Parameter ID = 'MeetingID' Value = '{2}' /><Parameter ID = 'EndTime' Value = '{3}' /></Parameters></RequestAction>" {
, requestID, RoomGuid, meeting.MeetingID, extendMinutes); Debug.Console(1, this, "No meeting in progress. Unable to modify end time.");
return;
}
#warning Need to add logic to properly extend from the current time. See S+ module for reference.
if (extendMinutes > -1)
{
if(extendMinutes > 0)
{
var extendTime = CurrentMeeting.dtEnd - DateTime.Now;
double extendMinutesRaw = extendTime.TotalMinutes;
Debug.Console(1, this, "Sending MeetingChange Request: \n{0}", requestTest); extendMinutes = extendMinutes + (int)Math.Round(extendMinutesRaw);
}
string requestTest = string.Format(
"<RequestAction><RequestID>{0}</RequestID><RoomID>{1}</RoomID><ActionID>MeetingChange</ActionID><Parameters><Parameter ID = 'MeetingID' Value = '{2}' /><Parameter ID = 'EndTime' Value = '{3}' /></Parameters></RequestAction>"
, requestID, RoomGuid, CurrentMeeting.MeetingID, extendMinutes);
Debug.Console(1, this, "Sending MeetingChange Request: \n{0}", requestTest);
FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = requestTest;
}
else
{
Debug.Console(1, this, "Invalid time specified");
}
//FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest;
FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = requestTest;
} }
/// <summary>
/// Creates and Ad Hoc meeting with a duration of 1 hour, or until the next meeting if in less than 1 hour.
/// </summary>
public void CreateAsHocMeeting(string command)
{
string requestID = "CreateAdHocMeeting";
DateTime now = DateTime.Now.AddMinutes(1);
now.AddSeconds(-now.Second);
// Assume 1 hour meeting if possible
DateTime dtEnd = now.AddHours(1);
// Check if room is available for 1 hour before next meeting
if (NextMeeting != null)
{
var roomAvailable = NextMeeting.dtEnd.Subtract(dtEnd);
if (roomAvailable.TotalMinutes < 60)
{
/// Room not available for full hour, book until next meeting starts
dtEnd = NextMeeting.dtEnd;
}
}
string createMeetingRequest =
"<CreateSchedule>" +
string.Format("<RequestID>{0}</RequestID>", requestID) +
string.Format("<RoomID>{0}</RoomID>", RoomGuid) +
"<Event>" +
string.Format("<dtStart>{0}</dtStart>", now.ToString("s")) +
string.Format("<dtEnd>{0}</dtEnd>", dtEnd.ToString("s")) +
"<Subject>AdHoc Meeting</Subject>" +
"<Organizer>Room User</Organizer>" +
"<Body></Body>" +
"</Event>" +
"</CreateSchedule>";
Debug.Console(1, this, "Sending CreateMeeting Request: \n{0}", createMeetingRequest);
FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = createMeetingRequest;
}
/// <summary>
/// Event handler method for Device Extender sig changes
/// </summary>
/// <param name="currentDeviceExtender"></param>
/// <param name="args"></param>
void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args)
{ {
Debug.Console(1, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); Debug.Console(1, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue);
@@ -396,28 +476,80 @@ namespace PepperDash.Essentials.Fusion
{ {
try try
{ {
ActionResponse actionResponse = new ActionResponse(); //ActionResponse actionResponse = new ActionResponse();
TextReader reader = new StringReader(args.Sig.StringValue); //TextReader reader = new StringReader(args.Sig.StringValue);
actionResponse = CrestronXMLSerialization.DeSerializeObject<ActionResponse>(reader); //actionResponse = CrestronXMLSerialization.DeSerializeObject<ActionResponse>(reader);
if (actionResponse.RequestID == "InitialPushRequest") //if (actionResponse != null)
//{
// if (actionResponse.RequestID == "InitialPushRequest")
// {
// if (actionResponse.Parameters != null)
// {
// var tempParam = actionResponse.Parameters.FirstOrDefault(p => p.ID.Equals("Registered"));
XmlDocument message = new XmlDocument();
message.LoadXml(args.Sig.StringValue);
var actionResponse = message["ActionResponse"];
if (actionResponse != null)
{ {
var tempParam = actionResponse.Parameters.FirstOrDefault(p => p.ID.Equals("Registered")); var requestID = actionResponse["RequestID"];
if (tempParam != null) if (requestID.InnerText == "InitialPushRequest")
{ {
if (tempParam.Value = 1) if (actionResponse["ActionID"].InnerText == "RegisterPushModel")
IsRegisteredForSchedulePushNotifications = true;
else
{ {
IsRegisteredForSchedulePushNotifications = false; var parameters = actionResponse["Parameters"];
foreach (XmlElement parameter in parameters)
{
if (parameter.HasAttributes)
{
var attributes = parameter.Attributes;
if (attributes["ID"].Value == "Registered")
{
var isRegistered = Int32.Parse(attributes["Value"].Value);
if (isRegistered == 1)
{
IsRegisteredForSchedulePushNotifications = true;
if (PollTimer != null && !PollTimer.Disposed)
{
PollTimer.Stop();
PollTimer.Dispose();
}
PushNotificationTimer = new CTimer(RequestFullRoomSchedule, null, PushNotificationTimeout, PushNotificationTimeout);
PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout);
}
else if (isRegistered == 0)
{
IsRegisteredForSchedulePushNotifications = false;
if (PushNotificationTimer != null && !PushNotificationTimer.Disposed)
{
PushNotificationTimer.Stop();
PushNotificationTimer.Dispose();
}
PollTimer = new CTimer(RequestFullRoomSchedule, null, SchedulePollInterval, SchedulePollInterval);
PollTimer.Reset(SchedulePollInterval, SchedulePollInterval);
}
}
}
}
} }
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
@@ -426,9 +558,14 @@ namespace PepperDash.Essentials.Fusion
} }
} }
/// <summary>
/// Event handler method for Device Extender sig changes
/// </summary>
/// <param name="currentDeviceExtender"></param>
/// <param name="args"></param>
void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args)
{ {
Debug.Console(1, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); Debug.Console(1, this, "Scehdule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue);
if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse) if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse)
@@ -441,22 +578,27 @@ namespace PepperDash.Essentials.Fusion
message.LoadXml(args.Sig.StringValue); message.LoadXml(args.Sig.StringValue);
var scheuldResponse = message["ScheduleRespones"]; var response = message["ScheduleResponse"];
if (scheuldResponse != null) if (response != null)
{ {
// Check for push notification // Check for push notification
if (scheuldResponse["RequestID"].InnerText == "RVRequest") if (response["RequestID"].InnerText == "RVRequest")
{ {
var action = scheuldResponse["Action"]; var action = response["Action"];
if (action.InnerText.IndexOf("RequestSchedule") > -1) if (action.OuterXml.IndexOf("RequestSchedule") > -1)
{ {
RequestFullRoomSchedule("PushScheduleRefresh"); PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout);
} }
} }
else // Not a push notification else // Not a push notification
{ {
CurrentSchedule = new RoomSchedule(); // Clear Current Schedule
CurrentMeeting = null; // Clear Current Meeting
NextMeeting = null; // Clear Next Meeting
bool isNextMeeting = false;
foreach (XmlElement element in message.FirstChild.ChildNodes) foreach (XmlElement element in message.FirstChild.ChildNodes)
{ {
@@ -483,15 +625,33 @@ namespace PepperDash.Essentials.Fusion
tempEvent = CrestronXMLSerialization.DeSerializeObject<Event>(reader); tempEvent = CrestronXMLSerialization.DeSerializeObject<Event>(reader);
scheduleResponse.Events.Add(tempEvent); scheduleResponse.Events.Add(tempEvent);
// Check is this is the current event
if (tempEvent.dtStart <= DateTime.Now && tempEvent.dtEnd >= DateTime.Now)
{
CurrentMeeting = tempEvent; // Set Current Meeting
isNextMeeting = true; // Flag that next element is next meeting
}
if (isNextMeeting)
{
NextMeeting = tempEvent; // Set Next Meeting
isNextMeeting = false;
}
CurrentSchedule.Meetings.Add(tempEvent);
} }
} }
PrintTodaysSchedule();
if (!IsRegisteredForSchedulePushNotifications)
PollTimer.Reset(SchedulePollInterval, SchedulePollInterval);
} }
} }
CurrentSchedule = scheduleResponse;
PrintTodaysSchedule();
} }
catch (Exception e) catch (Exception e)
@@ -499,22 +659,26 @@ namespace PepperDash.Essentials.Fusion
Debug.Console(1, this, "Error parsing ScheduleResponse: {0}", e); Debug.Console(1, this, "Error parsing ScheduleResponse: {0}", e);
} }
} }
else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse)
{
Debug.Console(1, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue);
}
} }
void PrintTodaysSchedule() void PrintTodaysSchedule()
{ {
if (CurrentSchedule.Events.Count > 0) if (CurrentSchedule.Meetings.Count > 0)
{ {
Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name); Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name);
foreach (Event e in CurrentSchedule.Events) foreach (Event e in CurrentSchedule.Meetings)
{ {
Debug.Console(1, this, "Subject: {0}", e.Subject); Debug.Console(1, this, "Subject: {0}", e.Subject);
Debug.Console(1, this, "Organizer: {0}", e.Organizer); Debug.Console(1, this, "Organizer: {0}", e.Organizer);
Debug.Console(1, this, "MeetingID: {0}", e.MeetingID); Debug.Console(1, this, "MeetingID: {0}", e.MeetingID);
Debug.Console(1, this, "Start Time: {0}", e.dtStart); Debug.Console(1, this, "Start Time: {0}", e.dtStart);
Debug.Console(1, this, "End Time: {0}", e.dtEnd); Debug.Console(1, this, "End Time: {0}", e.dtEnd);
var duration = e.dtEnd.Subtract(e.dtStart);
Debug.Console(1, this, "Duration: {0}\n", e.DurationInMinutes); Debug.Console(1, this, "Duration: {0}\n", e.DurationInMinutes);
} }
} }
@@ -945,12 +1109,21 @@ namespace PepperDash.Essentials.Fusion
} }
} }
//***************************************************************************************************
public class RoomSchedule
{
public List<Event> Meetings { get; set; }
public RoomSchedule()
{
Meetings = new List<Event>();
}
}
//**************************************************************************************************** //****************************************************************************************************
// Helper Classes for XML API // Helper Classes for XML API
/// <summary> /// <summary>
/// All the data needed for a full schedule request in a room /// All the data needed for a full schedule request in a room
/// </summary> /// </summary>

View File

@@ -134,6 +134,7 @@
<Compile Include="Devices\DiscPlayer\OppoExtendedBdp.cs" /> <Compile Include="Devices\DiscPlayer\OppoExtendedBdp.cs" />
<Compile Include="Devices\NUMERIC AppleTV.cs" /> <Compile Include="Devices\NUMERIC AppleTV.cs" />
<Compile Include="ControlSystem.cs" /> <Compile Include="ControlSystem.cs" />
<Compile Include="Fusion\FusionEventHandlers.cs" />
<Compile Include="REMOVE EssentialsApp.cs" /> <Compile Include="REMOVE EssentialsApp.cs" />
<Compile Include="Fusion\FusionSystemController.cs" /> <Compile Include="Fusion\FusionSystemController.cs" />
<Compile Include="HttpApiHandler.cs" /> <Compile Include="HttpApiHandler.cs" />