diff --git a/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs b/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs index f4a99b04..361697da 100644 --- a/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs +++ b/Essentials Devices Common/Essentials Devices Common/Factory/DeviceFactory.cs @@ -100,6 +100,12 @@ namespace PepperDash.Essentials.Devices.Common return new Laptop(key, name); } + else if (typeName == "mockvc") + { + return new PepperDash.Essentials.Devices.Common.VideoCodec + .MockVC(key, name); + } + else if (groupName == "settopbox") //(typeName == "irstbbase") { var irCont = IRPortHelper.GetIrOutputPortController(dc); diff --git a/Essentials Devices Common/Essentials Devices Common/VC/MockVC/MockVC.cs b/Essentials Devices Common/Essentials Devices Common/VC/MockVC/MockVC.cs index d8f11150..29b5832c 100644 --- a/Essentials Devices Common/Essentials Devices Common/VC/MockVC/MockVC.cs +++ b/Essentials Devices Common/Essentials Devices Common/VC/MockVC/MockVC.cs @@ -52,7 +52,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec /// public override void Dial(string s) { - + Debug.Console(1, this, "Dial: {0}", s); + _InCall = true; InCallFeedback.FireUpdate(); } @@ -63,12 +64,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec /// public void SendDTMF(string s) { - + Debug.Console(1, this, "SendDTMF: {0}", s); } - + /// + /// + /// public override void EndCall() { + Debug.Console(1, this, "EndCall"); _InCall = false; InCallFeedback.FireUpdate(); } @@ -78,7 +82,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec /// public override void AcceptCall() { - + Debug.Console(1, this, "AcceptCall"); } /// @@ -86,52 +90,84 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec /// public override void RejectCall() { - + Debug.Console(1, this, "RejectCall"); } - + /// + /// Called by routing to make it happen + /// + /// public override void ExecuteSwitch(object selector) { - + Debug.Console(1, this, "ExecuteSwitch"); + } + /// + /// + /// public override void ReceiveMuteOff() { + Debug.Console(1, this, "ReceiveMuteOff"); + if (!_ReceiveMute) return; _ReceiveMute = false; ReceiveMuteIsOnFeedback.FireUpdate(); } + /// + /// + /// public override void ReceiveMuteOn() { + Debug.Console(1, this, "ReceiveMuteOn"); if (_ReceiveMute) return; ReceiveMuteIsOnFeedback.FireUpdate(); } + /// + /// + /// public override void ReceiveMuteToggle() { + Debug.Console(1, this, "ReceiveMuteToggle"); + _ReceiveMute = !_ReceiveMute; ReceiveMuteIsOnFeedback.FireUpdate(); } + /// + /// + /// + /// public override void SetReceiveVolume(ushort level) { - + Debug.Console(1, this, "SetReceiveVolume: {0}", level); + } + /// + /// + /// public override void TransmitMuteOff() { + Debug.Console(1, this, "TransmitMuteOff"); + if (!_TransmitMute) return; _TransmitMute = false; TransmitMuteIsOnFeedback.FireUpdate(); } + /// + /// + /// public override void TransmitMuteOn() { + Debug.Console(1, this, "TransmitMuteOn"); if (_TransmitMute) return; TransmitMuteIsOnFeedback.FireUpdate(); @@ -140,16 +176,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec public override void TransmitMuteToggle() { _TransmitMute = !_TransmitMute; + Debug.Console(1, this, "TransmitMuteToggle: {0}", _TransmitMute); TransmitMuteIsOnFeedback.FireUpdate(); } public override void SetTransmitVolume(ushort level) { - + Debug.Console(1, this, "SetTransmitVolume: {0}", level); } public override void PrivacyModeOn() { + Debug.Console(1, this, "PrivacyMuteOn"); if (_PrivacyModeIsOn) return; _PrivacyModeIsOn = true; @@ -159,6 +197,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec public override void PrivacyModeOff() { + Debug.Console(1, this, "PrivacyMuteOff"); if (!_PrivacyModeIsOn) return; _PrivacyModeIsOn = false; @@ -168,7 +207,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec public override void PrivacyModeToggle() { _PrivacyModeIsOn = !_PrivacyModeIsOn; - PrivacyModeIsOnFeedback.FireUpdate(); + Debug.Console(1, this, "PrivacyMuteToggle: {0}", _PrivacyModeIsOn); + PrivacyModeIsOnFeedback.FireUpdate(); } //******************************************************** @@ -176,16 +216,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec public void TestIncomingCall(string url) { + Debug.Console(1, this, "TestIncomingCall"); + _IncomingCall = true; IncomingCallFeedback.FireUpdate(); } public void TestFarEndHangup() { + Debug.Console(1, this, "TestFarEndHangup"); } - - - } } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Config/DeviceFactory.cs b/Essentials/PepperDashEssentials/Config/DeviceFactory.cs index d87e41b7..7a8dfdb9 100644 --- a/Essentials/PepperDashEssentials/Config/DeviceFactory.cs +++ b/Essentials/PepperDashEssentials/Config/DeviceFactory.cs @@ -35,6 +35,7 @@ namespace PepperDash.Essentials properties.ToString()); return new EssentialsTouchpanelController(key, name, typeName, props, comm.IpIdInt); } + else if (typeName == "mockdisplay") { return new MockDisplay(key, name); diff --git a/Essentials/PepperDashEssentials/Fusion/FusionEventHandlers.cs b/Essentials/PepperDashEssentials/OTHER/Fusion/FusionEventHandlers.cs similarity index 95% rename from Essentials/PepperDashEssentials/Fusion/FusionEventHandlers.cs rename to Essentials/PepperDashEssentials/OTHER/Fusion/FusionEventHandlers.cs index 7c7b5168..4987c047 100644 --- a/Essentials/PepperDashEssentials/Fusion/FusionEventHandlers.cs +++ b/Essentials/PepperDashEssentials/OTHER/Fusion/FusionEventHandlers.cs @@ -1,18 +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 Schedule { get; set; } - } - - public class MeetingChangeEventArgs : EventArgs - { - public Event Meeting { get; set; } - } +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 Schedule { get; set; } + } + + public class MeetingChangeEventArgs : EventArgs + { + public Event Meeting { get; set; } + } } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Fusion/FusionProcessorQueries.cs b/Essentials/PepperDashEssentials/OTHER/Fusion/FusionProcessorQueries.cs similarity index 97% rename from Essentials/PepperDashEssentials/Fusion/FusionProcessorQueries.cs rename to Essentials/PepperDashEssentials/OTHER/Fusion/FusionProcessorQueries.cs index 9781fe08..8757be62 100644 --- a/Essentials/PepperDashEssentials/Fusion/FusionProcessorQueries.cs +++ b/Essentials/PepperDashEssentials/OTHER/Fusion/FusionProcessorQueries.cs @@ -1,63 +1,63 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using PepperDash.Core; -using PepperDash.Essentials.Core; - -namespace PepperDash.Essentials.Fusion -{ - /// - /// When created, runs progcomments on every slot and stores the program names in a list - /// - public class ProcessorProgReg - { - //public static Dictionary Programs { get; private set; } - - public static Dictionary GetProcessorProgReg() - { - var programs = new Dictionary(); - for (int i = 1; i <= Global.ControlSystem.NumProgramsSupported; i++) - { - string response = null; - var success = CrestronConsole.SendControlSystemCommand("progcomments:" + i, ref response); - var item = new ProcessorProgramItem(); - if (!success) - item.Name = "Error: PROGCOMMENTS failed"; - else - { - if (response.ToLower().Contains("bad or incomplete")) - item.Name = ""; - else - { - var startPos = response.IndexOf("Program File"); - var colonPos = response.IndexOf(":", startPos) + 1; - var endPos = response.IndexOf(CrestronEnvironment.NewLine, colonPos); - item.Name = response.Substring(colonPos, endPos - colonPos).Trim(); - item.Exists = true; - if (item.Name.Contains(".dll")) - { - startPos = response.IndexOf("Compiler Revision"); - colonPos = response.IndexOf(":", startPos) + 1; - endPos = response.IndexOf(CrestronEnvironment.NewLine, colonPos); - item.Name = item.Name + "_v" + response.Substring(colonPos, endPos - colonPos).Trim(); - } - } - } - programs[i] = item; - Debug.Console(1, "Program {0}: {1}", i, item.Name); - } - return programs; - } - } - - /// - /// Used in ProcessorProgReg - /// - public class ProcessorProgramItem - { - public bool Exists { get; set; } - public string Name { get; set; } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Fusion +{ + /// + /// When created, runs progcomments on every slot and stores the program names in a list + /// + public class ProcessorProgReg + { + //public static Dictionary Programs { get; private set; } + + public static Dictionary GetProcessorProgReg() + { + var programs = new Dictionary(); + for (int i = 1; i <= Global.ControlSystem.NumProgramsSupported; i++) + { + string response = null; + var success = CrestronConsole.SendControlSystemCommand("progcomments:" + i, ref response); + var item = new ProcessorProgramItem(); + if (!success) + item.Name = "Error: PROGCOMMENTS failed"; + else + { + if (response.ToLower().Contains("bad or incomplete")) + item.Name = ""; + else + { + var startPos = response.IndexOf("Program File"); + var colonPos = response.IndexOf(":", startPos) + 1; + var endPos = response.IndexOf(CrestronEnvironment.NewLine, colonPos); + item.Name = response.Substring(colonPos, endPos - colonPos).Trim(); + item.Exists = true; + if (item.Name.Contains(".dll")) + { + startPos = response.IndexOf("Compiler Revision"); + colonPos = response.IndexOf(":", startPos) + 1; + endPos = response.IndexOf(CrestronEnvironment.NewLine, colonPos); + item.Name = item.Name + "_v" + response.Substring(colonPos, endPos - colonPos).Trim(); + } + } + } + programs[i] = item; + Debug.Console(1, "Program {0}: {1}", i, item.Name); + } + return programs; + } + } + + /// + /// Used in ProcessorProgReg + /// + public class ProcessorProgramItem + { + public bool Exists { get; set; } + public string Name { get; set; } + } } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Fusion/FusionRviDataClasses.cs b/Essentials/PepperDashEssentials/OTHER/Fusion/FusionRviDataClasses.cs similarity index 96% rename from Essentials/PepperDashEssentials/Fusion/FusionRviDataClasses.cs rename to Essentials/PepperDashEssentials/OTHER/Fusion/FusionRviDataClasses.cs index e08d806f..3752531a 100644 --- a/Essentials/PepperDashEssentials/Fusion/FusionRviDataClasses.cs +++ b/Essentials/PepperDashEssentials/OTHER/Fusion/FusionRviDataClasses.cs @@ -1,499 +1,499 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro.Fusion; - -using PepperDash.Core; - -namespace PepperDash.Essentials.Fusion -{ - // Helper Classes for GUIDs - - /// - /// Stores GUIDs to be written to a file in NVRAM - /// - public class FusionRoomGuids - { - public string RoomName { get; set; } - public uint IpId { get; set; } - public string RoomGuid { get; set; } - public FusionOccupancySensorAsset OccupancyAsset { get; set; } - public Dictionary StaticAssets { get; set; } - - public FusionRoomGuids() - { - StaticAssets = new Dictionary(); - OccupancyAsset = new FusionOccupancySensorAsset(); - } - - public FusionRoomGuids(string roomName, uint ipId, string roomGuid, Dictionary staticAssets) - { - RoomName = roomName; - IpId = ipId; - RoomGuid = roomGuid; - - StaticAssets = staticAssets; - OccupancyAsset = new FusionOccupancySensorAsset(); - } - - public FusionRoomGuids(string roomName, uint ipId, string roomGuid, Dictionary staticAssets, FusionOccupancySensorAsset occAsset) - { - RoomName = roomName; - IpId = ipId; - RoomGuid = roomGuid; - - StaticAssets = staticAssets; - OccupancyAsset = occAsset; - } - - /// - /// Generates a new room GUID prefixed by the program slot number and NIC MAC address - /// - /// - /// - public string GenerateNewRoomGuid(uint progSlot, string mac) - { - Guid roomGuid = Guid.NewGuid(); - - return string.Format("{0}-{1}-{2}", progSlot, mac, roomGuid.ToString()); - } - - - /// - /// Adds an asset to the StaticAssets collection and returns the new asset - /// - /// - /// - /// - /// - /// - /// - public FusionAsset AddStaticAsset(FusionRoom room, int uid, string assetName, string type, string instanceId) - { - var slotNum = GetNextAvailableAssetNumber(room); - - Debug.Console(2, "Adding Fusion Asset: {0} of Type: {1} at Slot Number: {2} with GUID: {3}", assetName, type, slotNum, instanceId); - - var tempAsset = new FusionAsset(slotNum, assetName, type, instanceId); - - StaticAssets.Add(uid, tempAsset); - - return tempAsset; - } - - /// - /// Returns the next available slot number in the Fusion UserConfigurableAssetDetails collection - /// - /// - /// - public static uint GetNextAvailableAssetNumber(FusionRoom room) - { - uint slotNum = 0; - - foreach (var item in room.UserConfigurableAssetDetails) - { - if(item.Number > slotNum) - slotNum = item.Number; - } - - if (slotNum < 5) - { - slotNum = 5; - } - else - slotNum = slotNum + 1; - - Debug.Console(2, "#Next available fusion asset number is: {0}", slotNum); - - return slotNum; - } - - } - - public class FusionOccupancySensorAsset - { - // SlotNumber fixed at 4 - - public uint SlotNumber { get { return 4; } } - public string Name { get { return "Occupancy Sensor"; } } - public eAssetType Type { get; set; } - public string InstanceId { get; set; } - - public FusionOccupancySensorAsset() - { - } - - public FusionOccupancySensorAsset(eAssetType type) - { - Type = type; - - InstanceId = Guid.NewGuid().ToString(); - } - } - - public class FusionAsset - { - public uint SlotNumber { get; set; } - public string Name { get; set; } - public string Type { get; set; } - public string InstanceId { get;set; } - - public FusionAsset() - { - - } - - public FusionAsset(uint slotNum, string assetName, string type, string instanceId) - { - SlotNumber = slotNum; - Name = assetName; - Type = type; - if (string.IsNullOrEmpty(instanceId)) - { - InstanceId = Guid.NewGuid().ToString(); - } - else - { - InstanceId = instanceId; - } - } - } - - //*************************************************************************************************** - - public class RoomSchedule - { - public List Meetings { get; set; } - - public RoomSchedule() - { - Meetings = new List(); - } - } - - //**************************************************************************************************** - // Helper Classes for XML API - - /// - /// Data needed to request the local time from the Fusion server - /// - public class LocalTimeRequest - { - public string RequestID { get; set; } - } - - /// - /// All the data needed for a full schedule request in a room - /// - /// //[XmlRoot(ElementName = "RequestSchedule")] - public class RequestSchedule - { - //[XmlElement(ElementName = "RequestID")] - public string RequestID { get; set; } - //[XmlElement(ElementName = "RoomID")] - public string RoomID { get; set; } - //[XmlElement(ElementName = "Start")] - public DateTime Start { get; set; } - //[XmlElement(ElementName = "HourSpan")] - public double HourSpan { get; set; } - - public RequestSchedule(string requestID, string roomID) - { - RequestID = requestID; - RoomID = roomID; - Start = DateTime.Now; - HourSpan = 24; - } - } - - - //[XmlRoot(ElementName = "RequestAction")] - public class RequestAction - { - //[XmlElement(ElementName = "RequestID")] - public string RequestID { get; set; } - //[XmlElement(ElementName = "RoomID")] - public string RoomID { get; set; } - //[XmlElement(ElementName = "ActionID")] - public string ActionID { get; set; } - //[XmlElement(ElementName = "Parameters")] - public List Parameters { get; set; } - - public RequestAction(string roomID, string actionID, List parameters) - { - RoomID = roomID; - ActionID = actionID; - Parameters = parameters; - } - } - - //[XmlRoot(ElementName = "ActionResponse")] - public class ActionResponse - { - //[XmlElement(ElementName = "RequestID")] - public string RequestID { get; set; } - //[XmlElement(ElementName = "ActionID")] - public string ActionID { get; set; } - //[XmlElement(ElementName = "Parameters")] - public List Parameters { get; set; } - } - - //[XmlRoot(ElementName = "Parameter")] - public class Parameter - { - //[XmlAttribute(AttributeName = "ID")] - public string ID { get; set; } - //[XmlAttribute(AttributeName = "Value")] - public string Value { get; set; } - } - - ////[XmlRoot(ElementName = "Parameters")] - //public class Parameters - //{ - // //[XmlElement(ElementName = "Parameter")] - // public List Parameter { get; set; } - //} - - /// - /// Data structure for a ScheduleResponse from Fusion - /// - /// //[XmlRoot(ElementName = "ScheduleResponse")] - public class ScheduleResponse - { - //[XmlElement(ElementName = "RequestID")] - public string RequestID { get; set; } - //[XmlElement(ElementName = "RoomID")] - public string RoomID { get; set; } - //[XmlElement(ElementName = "RoomName")] - public string RoomName { get; set; } - //[XmlElement("Event")] - public List Events { get; set; } - - public ScheduleResponse() - { - Events = new List(); - } - } - - //[XmlRoot(ElementName = "Event")] - public class Event - { - //[XmlElement(ElementName = "MeetingID")] - public string MeetingID { get; set; } - //[XmlElement(ElementName = "RVMeetingID")] - public string RVMeetingID { get; set; } - //[XmlElement(ElementName = "Recurring")] - public string Recurring { get; set; } - //[XmlElement(ElementName = "InstanceID")] - public string InstanceID { get; set; } - //[XmlElement(ElementName = "dtStart")] - public DateTime dtStart { get; set; } - //[XmlElement(ElementName = "dtEnd")] - public DateTime dtEnd { get; set; } - //[XmlElement(ElementName = "Organizer")] - public string Organizer { get; set; } - //[XmlElement(ElementName = "Attendees")] - public Attendees Attendees { get; set; } - //[XmlElement(ElementName = "Resources")] - public Resources Resources { get; set; } - //[XmlElement(ElementName = "IsEvent")] - public string IsEvent { get; set; } - //[XmlElement(ElementName = "IsRoomViewMeeting")] - public string IsRoomViewMeeting { get; set; } - //[XmlElement(ElementName = "IsPrivate")] - public string IsPrivate { get; set; } - //[XmlElement(ElementName = "IsExchangePrivate")] - public string IsExchangePrivate { get; set; } - //[XmlElement(ElementName = "MeetingTypes")] - public MeetingTypes MeetingTypes { get; set; } - //[XmlElement(ElementName = "ParticipantCode")] - public string ParticipantCode { get; set; } - //[XmlElement(ElementName = "PhoneNo")] - public string PhoneNo { get; set; } - //[XmlElement(ElementName = "WelcomeMsg")] - public string WelcomeMsg { get; set; } - //[XmlElement(ElementName = "Subject")] - public string Subject { get; set; } - //[XmlElement(ElementName = "LiveMeeting")] - public LiveMeeting LiveMeeting { get; set; } - //[XmlElement(ElementName = "ShareDocPath")] - public string ShareDocPath { get; set; } - //[XmlElement(ElementName = "HaveAttendees")] - public string HaveAttendees { get; set; } - //[XmlElement(ElementName = "HaveResources")] - public string HaveResources { get; set; } - - /// - /// Gets the duration of the meeting - /// - public string DurationInMinutes - { - get - { - string duration; - - var timeSpan = dtEnd.Subtract(dtStart); - int hours = timeSpan.Hours; - double minutes = timeSpan.Minutes; - double roundedMinutes = Math.Round(minutes); - if (hours > 0) - { - duration = string.Format("{0} hours {1} minutes", hours, roundedMinutes); - } - else - { - duration = string.Format("{0} minutes", roundedMinutes); - } - - return duration; - } - } - - /// - /// Gets the remaining time in the meeting. Returns null if the meeting is not currently in progress. - /// - public string RemainingTime - { - get - { - var now = DateTime.Now; - - string remainingTime; - - if (GetInProgress()) - { - var timeSpan = dtEnd.Subtract(now); - int hours = timeSpan.Hours; - double minutes = timeSpan.Minutes; - double roundedMinutes = Math.Round(minutes); - if (hours > 0) - { - remainingTime = string.Format("{0} hours {1} minutes", hours, roundedMinutes); - } - else - { - remainingTime = string.Format("{0} minutes", roundedMinutes); - } - - return remainingTime; - } - else - return null; - } - - } - - /// - /// Indicates that the meeting is in progress - /// - public bool isInProgress - { - get - { - return GetInProgress(); - } - } - - /// - /// Determines if the meeting is in progress - /// - /// Returns true if in progress - bool GetInProgress() - { - var now = DateTime.Now; - - if (now > dtStart && now < dtEnd) - { - return true; - } - else - return false; - } - } - - //[XmlRoot(ElementName = "Resources")] - public class Resources - { - //[XmlElement(ElementName = "Rooms")] - public Rooms Rooms { get; set; } - } - - //[XmlRoot(ElementName = "Rooms")] - public class Rooms - { - //[XmlElement(ElementName = "Room")] - public List Room { get; set; } - } - - //[XmlRoot(ElementName = "Room")] - public class Room - { - //[XmlElement(ElementName = "Name")] - public string Name { get; set; } - //[XmlElement(ElementName = "ID")] - public string ID { get; set; } - //[XmlElement(ElementName = "MPType")] - public string MPType { get; set; } - } - - //[XmlRoot(ElementName = "Attendees")] - public class Attendees - { - //[XmlElement(ElementName = "Required")] - public Required Required { get; set; } - //[XmlElement(ElementName = "Optional")] - public Optional Optional { get; set; } - } - - //[XmlRoot(ElementName = "Required")] - public class Required - { - //[XmlElement(ElementName = "Attendee")] - public List Attendee { get; set; } - } - - //[XmlRoot(ElementName = "Optional")] - public class Optional - { - //[XmlElement(ElementName = "Attendee")] - public List Attendee { get; set; } - } - - //[XmlRoot(ElementName = "MeetingType")] - public class MeetingType - { - //[XmlAttribute(AttributeName = "ID")] - public string ID { get; set; } - //[XmlAttribute(AttributeName = "Value")] - public string Value { get; set; } - } - - //[XmlRoot(ElementName = "MeetingTypes")] - public class MeetingTypes - { - //[XmlElement(ElementName = "MeetingType")] - public List MeetingType { get; set; } - } - - //[XmlRoot(ElementName = "LiveMeeting")] - public class LiveMeeting - { - //[XmlElement(ElementName = "URL")] - public string URL { get; set; } - //[XmlElement(ElementName = "ID")] - public string ID { get; set; } - //[XmlElement(ElementName = "Key")] - public string Key { get; set; } - //[XmlElement(ElementName = "Subject")] - public string Subject { get; set; } - } - - //[XmlRoot(ElementName = "LiveMeetingURL")] - public class LiveMeetingURL - { - //[XmlElement(ElementName = "LiveMeeting")] - public LiveMeeting LiveMeeting { get; set; } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.Fusion; + +using PepperDash.Core; + +namespace PepperDash.Essentials.Fusion +{ + // Helper Classes for GUIDs + + /// + /// Stores GUIDs to be written to a file in NVRAM + /// + public class FusionRoomGuids + { + public string RoomName { get; set; } + public uint IpId { get; set; } + public string RoomGuid { get; set; } + public FusionOccupancySensorAsset OccupancyAsset { get; set; } + public Dictionary StaticAssets { get; set; } + + public FusionRoomGuids() + { + StaticAssets = new Dictionary(); + OccupancyAsset = new FusionOccupancySensorAsset(); + } + + public FusionRoomGuids(string roomName, uint ipId, string roomGuid, Dictionary staticAssets) + { + RoomName = roomName; + IpId = ipId; + RoomGuid = roomGuid; + + StaticAssets = staticAssets; + OccupancyAsset = new FusionOccupancySensorAsset(); + } + + public FusionRoomGuids(string roomName, uint ipId, string roomGuid, Dictionary staticAssets, FusionOccupancySensorAsset occAsset) + { + RoomName = roomName; + IpId = ipId; + RoomGuid = roomGuid; + + StaticAssets = staticAssets; + OccupancyAsset = occAsset; + } + + /// + /// Generates a new room GUID prefixed by the program slot number and NIC MAC address + /// + /// + /// + public string GenerateNewRoomGuid(uint progSlot, string mac) + { + Guid roomGuid = Guid.NewGuid(); + + return string.Format("{0}-{1}-{2}", progSlot, mac, roomGuid.ToString()); + } + + + /// + /// Adds an asset to the StaticAssets collection and returns the new asset + /// + /// + /// + /// + /// + /// + /// + public FusionAsset AddStaticAsset(FusionRoom room, int uid, string assetName, string type, string instanceId) + { + var slotNum = GetNextAvailableAssetNumber(room); + + Debug.Console(2, "Adding Fusion Asset: {0} of Type: {1} at Slot Number: {2} with GUID: {3}", assetName, type, slotNum, instanceId); + + var tempAsset = new FusionAsset(slotNum, assetName, type, instanceId); + + StaticAssets.Add(uid, tempAsset); + + return tempAsset; + } + + /// + /// Returns the next available slot number in the Fusion UserConfigurableAssetDetails collection + /// + /// + /// + public static uint GetNextAvailableAssetNumber(FusionRoom room) + { + uint slotNum = 0; + + foreach (var item in room.UserConfigurableAssetDetails) + { + if(item.Number > slotNum) + slotNum = item.Number; + } + + if (slotNum < 5) + { + slotNum = 5; + } + else + slotNum = slotNum + 1; + + Debug.Console(2, "#Next available fusion asset number is: {0}", slotNum); + + return slotNum; + } + + } + + public class FusionOccupancySensorAsset + { + // SlotNumber fixed at 4 + + public uint SlotNumber { get { return 4; } } + public string Name { get { return "Occupancy Sensor"; } } + public eAssetType Type { get; set; } + public string InstanceId { get; set; } + + public FusionOccupancySensorAsset() + { + } + + public FusionOccupancySensorAsset(eAssetType type) + { + Type = type; + + InstanceId = Guid.NewGuid().ToString(); + } + } + + public class FusionAsset + { + public uint SlotNumber { get; set; } + public string Name { get; set; } + public string Type { get; set; } + public string InstanceId { get;set; } + + public FusionAsset() + { + + } + + public FusionAsset(uint slotNum, string assetName, string type, string instanceId) + { + SlotNumber = slotNum; + Name = assetName; + Type = type; + if (string.IsNullOrEmpty(instanceId)) + { + InstanceId = Guid.NewGuid().ToString(); + } + else + { + InstanceId = instanceId; + } + } + } + + //*************************************************************************************************** + + public class RoomSchedule + { + public List Meetings { get; set; } + + public RoomSchedule() + { + Meetings = new List(); + } + } + + //**************************************************************************************************** + // Helper Classes for XML API + + /// + /// Data needed to request the local time from the Fusion server + /// + public class LocalTimeRequest + { + public string RequestID { get; set; } + } + + /// + /// All the data needed for a full schedule request in a room + /// + /// //[XmlRoot(ElementName = "RequestSchedule")] + public class RequestSchedule + { + //[XmlElement(ElementName = "RequestID")] + public string RequestID { get; set; } + //[XmlElement(ElementName = "RoomID")] + public string RoomID { get; set; } + //[XmlElement(ElementName = "Start")] + public DateTime Start { get; set; } + //[XmlElement(ElementName = "HourSpan")] + public double HourSpan { get; set; } + + public RequestSchedule(string requestID, string roomID) + { + RequestID = requestID; + RoomID = roomID; + Start = DateTime.Now; + HourSpan = 24; + } + } + + + //[XmlRoot(ElementName = "RequestAction")] + public class RequestAction + { + //[XmlElement(ElementName = "RequestID")] + public string RequestID { get; set; } + //[XmlElement(ElementName = "RoomID")] + public string RoomID { get; set; } + //[XmlElement(ElementName = "ActionID")] + public string ActionID { get; set; } + //[XmlElement(ElementName = "Parameters")] + public List Parameters { get; set; } + + public RequestAction(string roomID, string actionID, List parameters) + { + RoomID = roomID; + ActionID = actionID; + Parameters = parameters; + } + } + + //[XmlRoot(ElementName = "ActionResponse")] + public class ActionResponse + { + //[XmlElement(ElementName = "RequestID")] + public string RequestID { get; set; } + //[XmlElement(ElementName = "ActionID")] + public string ActionID { get; set; } + //[XmlElement(ElementName = "Parameters")] + public List Parameters { get; set; } + } + + //[XmlRoot(ElementName = "Parameter")] + public class Parameter + { + //[XmlAttribute(AttributeName = "ID")] + public string ID { get; set; } + //[XmlAttribute(AttributeName = "Value")] + public string Value { get; set; } + } + + ////[XmlRoot(ElementName = "Parameters")] + //public class Parameters + //{ + // //[XmlElement(ElementName = "Parameter")] + // public List Parameter { get; set; } + //} + + /// + /// Data structure for a ScheduleResponse from Fusion + /// + /// //[XmlRoot(ElementName = "ScheduleResponse")] + public class ScheduleResponse + { + //[XmlElement(ElementName = "RequestID")] + public string RequestID { get; set; } + //[XmlElement(ElementName = "RoomID")] + public string RoomID { get; set; } + //[XmlElement(ElementName = "RoomName")] + public string RoomName { get; set; } + //[XmlElement("Event")] + public List Events { get; set; } + + public ScheduleResponse() + { + Events = new List(); + } + } + + //[XmlRoot(ElementName = "Event")] + public class Event + { + //[XmlElement(ElementName = "MeetingID")] + public string MeetingID { get; set; } + //[XmlElement(ElementName = "RVMeetingID")] + public string RVMeetingID { get; set; } + //[XmlElement(ElementName = "Recurring")] + public string Recurring { get; set; } + //[XmlElement(ElementName = "InstanceID")] + public string InstanceID { get; set; } + //[XmlElement(ElementName = "dtStart")] + public DateTime dtStart { get; set; } + //[XmlElement(ElementName = "dtEnd")] + public DateTime dtEnd { get; set; } + //[XmlElement(ElementName = "Organizer")] + public string Organizer { get; set; } + //[XmlElement(ElementName = "Attendees")] + public Attendees Attendees { get; set; } + //[XmlElement(ElementName = "Resources")] + public Resources Resources { get; set; } + //[XmlElement(ElementName = "IsEvent")] + public string IsEvent { get; set; } + //[XmlElement(ElementName = "IsRoomViewMeeting")] + public string IsRoomViewMeeting { get; set; } + //[XmlElement(ElementName = "IsPrivate")] + public string IsPrivate { get; set; } + //[XmlElement(ElementName = "IsExchangePrivate")] + public string IsExchangePrivate { get; set; } + //[XmlElement(ElementName = "MeetingTypes")] + public MeetingTypes MeetingTypes { get; set; } + //[XmlElement(ElementName = "ParticipantCode")] + public string ParticipantCode { get; set; } + //[XmlElement(ElementName = "PhoneNo")] + public string PhoneNo { get; set; } + //[XmlElement(ElementName = "WelcomeMsg")] + public string WelcomeMsg { get; set; } + //[XmlElement(ElementName = "Subject")] + public string Subject { get; set; } + //[XmlElement(ElementName = "LiveMeeting")] + public LiveMeeting LiveMeeting { get; set; } + //[XmlElement(ElementName = "ShareDocPath")] + public string ShareDocPath { get; set; } + //[XmlElement(ElementName = "HaveAttendees")] + public string HaveAttendees { get; set; } + //[XmlElement(ElementName = "HaveResources")] + public string HaveResources { get; set; } + + /// + /// Gets the duration of the meeting + /// + public string DurationInMinutes + { + get + { + string duration; + + var timeSpan = dtEnd.Subtract(dtStart); + int hours = timeSpan.Hours; + double minutes = timeSpan.Minutes; + double roundedMinutes = Math.Round(minutes); + if (hours > 0) + { + duration = string.Format("{0} hours {1} minutes", hours, roundedMinutes); + } + else + { + duration = string.Format("{0} minutes", roundedMinutes); + } + + return duration; + } + } + + /// + /// Gets the remaining time in the meeting. Returns null if the meeting is not currently in progress. + /// + public string RemainingTime + { + get + { + var now = DateTime.Now; + + string remainingTime; + + if (GetInProgress()) + { + var timeSpan = dtEnd.Subtract(now); + int hours = timeSpan.Hours; + double minutes = timeSpan.Minutes; + double roundedMinutes = Math.Round(minutes); + if (hours > 0) + { + remainingTime = string.Format("{0} hours {1} minutes", hours, roundedMinutes); + } + else + { + remainingTime = string.Format("{0} minutes", roundedMinutes); + } + + return remainingTime; + } + else + return null; + } + + } + + /// + /// Indicates that the meeting is in progress + /// + public bool isInProgress + { + get + { + return GetInProgress(); + } + } + + /// + /// Determines if the meeting is in progress + /// + /// Returns true if in progress + bool GetInProgress() + { + var now = DateTime.Now; + + if (now > dtStart && now < dtEnd) + { + return true; + } + else + return false; + } + } + + //[XmlRoot(ElementName = "Resources")] + public class Resources + { + //[XmlElement(ElementName = "Rooms")] + public Rooms Rooms { get; set; } + } + + //[XmlRoot(ElementName = "Rooms")] + public class Rooms + { + //[XmlElement(ElementName = "Room")] + public List Room { get; set; } + } + + //[XmlRoot(ElementName = "Room")] + public class Room + { + //[XmlElement(ElementName = "Name")] + public string Name { get; set; } + //[XmlElement(ElementName = "ID")] + public string ID { get; set; } + //[XmlElement(ElementName = "MPType")] + public string MPType { get; set; } + } + + //[XmlRoot(ElementName = "Attendees")] + public class Attendees + { + //[XmlElement(ElementName = "Required")] + public Required Required { get; set; } + //[XmlElement(ElementName = "Optional")] + public Optional Optional { get; set; } + } + + //[XmlRoot(ElementName = "Required")] + public class Required + { + //[XmlElement(ElementName = "Attendee")] + public List Attendee { get; set; } + } + + //[XmlRoot(ElementName = "Optional")] + public class Optional + { + //[XmlElement(ElementName = "Attendee")] + public List Attendee { get; set; } + } + + //[XmlRoot(ElementName = "MeetingType")] + public class MeetingType + { + //[XmlAttribute(AttributeName = "ID")] + public string ID { get; set; } + //[XmlAttribute(AttributeName = "Value")] + public string Value { get; set; } + } + + //[XmlRoot(ElementName = "MeetingTypes")] + public class MeetingTypes + { + //[XmlElement(ElementName = "MeetingType")] + public List MeetingType { get; set; } + } + + //[XmlRoot(ElementName = "LiveMeeting")] + public class LiveMeeting + { + //[XmlElement(ElementName = "URL")] + public string URL { get; set; } + //[XmlElement(ElementName = "ID")] + public string ID { get; set; } + //[XmlElement(ElementName = "Key")] + public string Key { get; set; } + //[XmlElement(ElementName = "Subject")] + public string Subject { get; set; } + } + + //[XmlRoot(ElementName = "LiveMeetingURL")] + public class LiveMeetingURL + { + //[XmlElement(ElementName = "LiveMeeting")] + public LiveMeeting LiveMeeting { get; set; } + } } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs b/Essentials/PepperDashEssentials/OTHER/Fusion/FusionSystemController.cs similarity index 97% rename from Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs rename to Essentials/PepperDashEssentials/OTHER/Fusion/FusionSystemController.cs index f2568b8c..59b53112 100644 --- a/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs +++ b/Essentials/PepperDashEssentials/OTHER/Fusion/FusionSystemController.cs @@ -1,1394 +1,1394 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Crestron.SimplSharp.CrestronXml; -using Crestron.SimplSharp.CrestronXml.Serialization; -using Crestron.SimplSharp.CrestronXmlLinq; -using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.Fusion; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -using PepperDash.Core; -using PepperDash.Essentials; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Devices.Common; - - - -namespace PepperDash.Essentials.Fusion -{ - public class EssentialsHuddleSpaceFusionSystemController : Device - { - public event EventHandler ScheduleChange; - public event EventHandler MeetingEndWarning; - public event EventHandler NextMeetingBeginWarning; - - FusionRoom FusionRoom; - EssentialsHuddleSpaceRoom Room; - Dictionary SourceToFeedbackSigs = - new Dictionary(); - - //BooleanSigData OccupancyStatusSig; - - StatusMonitorCollection ErrorMessageRollUp; - - StringSigData CurrentRoomSourceNameSig; - - #region System Info Sigs - //StringSigData SystemName; - //StringSigData Model; - //StringSigData SerialNumber; - //StringSigData Uptime; - #endregion - - - #region Processor Info Sigs - StringSigData Ip1; - StringSigData Ip2; - StringSigData Gateway; - StringSigData Hostname; - StringSigData Domain; - StringSigData Dns1; - StringSigData Dns2; - StringSigData Mac1; - StringSigData Mac2; - StringSigData NetMask1; - StringSigData NetMask2; - StringSigData Firmware; - - StringSigData[] Program = new StringSigData[10]; - #endregion - - #region Default Display Source Sigs - - BooleanSigData[] Source = new BooleanSigData[10]; - - #endregion - - RoomSchedule CurrentSchedule; - - Event NextMeeting; - - Event CurrentMeeting; - - string RoomGuid - { - get - { - return GUIDs.RoomGuid; - } - - } - - uint IpId; - - FusionRoomGuids GUIDs; - - bool GuidFileExists; - - bool IsRegisteredForSchedulePushNotifications = false; - - CTimer PollTimer = null; - - CTimer PushNotificationTimer = null; - - // Default poll time is 5 min unless overridden by config value - public long SchedulePollInterval = 300000; - - public long PushNotificationTimeout = 5000; - - Dictionary FusionStaticAssets; - - FusionOccupancySensorAsset FusionOccSensor; - - //ScheduleResponseEvent NextMeeting; - - public EssentialsHuddleSpaceFusionSystemController(EssentialsHuddleSpaceRoom room, uint ipId) - : base(room.Key + "-fusion") - { - - Room = room; - - IpId = ipId; - - FusionStaticAssets = new Dictionary(); - - GUIDs = new FusionRoomGuids(); - - var mac = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); - - var slot = Global.ControlSystem.ProgramNumber; - - string guidFilePath = string.Format(@"\NVRAM\Program{0}\{1}-FusionGuids.json", Global.ControlSystem.ProgramNumber, InitialParametersClass.ProgramIDTag); - - GuidFileExists = File.Exists(guidFilePath); - - if (GuidFileExists) - { - ReadGuidFile(guidFilePath); - } - else - { - GUIDs = new FusionRoomGuids(Room.Name, ipId, GUIDs.GenerateNewRoomGuid(slot, mac), FusionStaticAssets); - } - - CreateSymbolAndBasicSigs(IpId); - SetUpSources(); - SetUpCommunitcationMonitors(); - SetUpDisplay(); - SetUpError(); - //SetUpOccupancy(); - - // Make it so! - FusionRVI.GenerateFileForAllFusionDevices(); - - GenerateGuidFile(guidFilePath); - } - - /// - /// Generates the guid file in NVRAM. If the file already exists it will be overwritten. - /// - /// path for the file - void GenerateGuidFile(string filePath) - { - if (string.IsNullOrEmpty(filePath)) - { - Debug.Console(0, this, "Error writing guid file. No path specified."); - return; - } - - CCriticalSection _fileLock = new CCriticalSection(); - - try - { - if (_fileLock == null || _fileLock.Disposed) - return; - - _fileLock.Enter(); - - Debug.Console(1, this, "Writing GUIDs to file"); - - if (FusionOccSensor == null) - GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets); - else - GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets, FusionOccSensor); - - var JSON = JsonConvert.SerializeObject(GUIDs, Newtonsoft.Json.Formatting.Indented); - - using (StreamWriter sw = new StreamWriter(filePath)) - { - sw.Write(JSON); - sw.Flush(); - } - - Debug.Console(1, this, "Guids successfully written to file '{0}'", filePath); - - } - catch (Exception e) - { - Debug.Console(0, this, "Error writing guid file: {0}", e); - } - finally - { - if (_fileLock != null && !_fileLock.Disposed) - _fileLock.Leave(); - } - } - - /// - /// Reads the guid file from NVRAM - /// - /// path for te file - void ReadGuidFile(string filePath) - { - if(string.IsNullOrEmpty(filePath)) - { - Debug.Console(0, this, "Error reading guid file. No path specified."); - return; - } - - CCriticalSection _fileLock = new CCriticalSection(); - - try - { - if(_fileLock == null || _fileLock.Disposed) - return; - - _fileLock.Enter(); - - if(File.Exists(filePath)) - { - var JSON = File.ReadToEnd(filePath, Encoding.ASCII); - - GUIDs = JsonConvert.DeserializeObject(JSON); - - IpId = GUIDs.IpId; - - FusionStaticAssets = GUIDs.StaticAssets; - - } - - Debug.Console(0, this, "Fusion Guids successfully read from file:"); - - Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, IpId, RoomGuid); - - foreach (KeyValuePair item in FusionStaticAssets) - { - Debug.Console(1, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name, item.Value.SlotNumber, item.Value.InstanceId); - } - } - catch (Exception e) - { - Debug.Console(0, this, "Error reading guid file: {0}", e); - } - finally - { - if(_fileLock != null && !_fileLock.Disposed) - _fileLock.Leave(); - } - - } - - void CreateSymbolAndBasicSigs(uint ipId) - { - Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); - - FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); - FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); - - FusionRoom.Register(); - - FusionRoom.FusionStateChange += new FusionStateEventHandler(FusionRoom_FusionStateChange); - - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(FusionRoomSchedule_DeviceExtenderSigChange); - FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange); - FusionRoom.OnlineStatusChange += new OnlineStatusChangeEventHandler(FusionRoom_OnlineStatusChange); - - 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(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); - - // Room to fusion room - Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); - - // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly); - // Don't think we need to get current status of this as nothing should be alive yet. - Room.CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSourceInfoChange); - - - FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction(Room.PowerOnToDefaultOrLastSource); - FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => Room.RunRouteAction("roomOff")); - // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); - FusionRoom.ErrorMessage.InputSig.StringValue = - "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;"; - - GetProcessorEthernetValues(); - - GetSystemInfo(); - - GetProcessorInfo(); - - CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); - } - - void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) - { - if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp) - { - GetProcessorEthernetValues(); - } - } - - void GetSystemInfo() - { - //SystemName.InputSig.StringValue = Room.Name; - //Model.InputSig.StringValue = InitialParametersClass.ControllerPromptName; - //SerialNumber.InputSig.StringValue = InitialParametersClass. - - string response = string.Empty; - - var systemReboot = FusionRoom.CreateOffsetBoolSig(74, "Processor - Reboot", eSigIoMask.OutputSigOnly); - systemReboot.OutputSig.SetSigFalseAction(() => CrestronConsole.SendControlSystemCommand("reboot", ref response)); - } - - void GetProcessorEthernetValues() - { - Ip1 = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - IP 1", eSigIoMask.InputSigOnly); - Ip2 = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - IP 2", eSigIoMask.InputSigOnly); - Gateway = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Gateway", eSigIoMask.InputSigOnly); - Hostname = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Hostname", eSigIoMask.InputSigOnly); - Domain = FusionRoom.CreateOffsetStringSig(54, "Info - Processor - Domain", eSigIoMask.InputSigOnly); - Dns1 = FusionRoom.CreateOffsetStringSig(55, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly); - Dns2 = FusionRoom.CreateOffsetStringSig(56, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly); - Mac1 = FusionRoom.CreateOffsetStringSig(57, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly); - Mac2 = FusionRoom.CreateOffsetStringSig(58, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly); - NetMask1 = FusionRoom.CreateOffsetStringSig(59, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly); - NetMask2 = FusionRoom.CreateOffsetStringSig(60, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly); - - // Interface =0 - Ip1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); - Gateway.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0); - Hostname.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); - Domain.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); - - var dnsServers = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, 0).Split(','); - Dns1.InputSig.StringValue = dnsServers[0]; - if (dnsServers.Length > 1) - Dns2.InputSig.StringValue = dnsServers[1]; - - Mac1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); - NetMask1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0); - - // Interface 1 - - if (InitialParametersClass.NumberOfEthernetInterfaces > 1) // Only get these values if the processor has more than 1 NIC - { - Ip2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1); - Mac2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 1); - NetMask2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 1); - } - } - - void GetProcessorInfo() - { - //SystemName = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - System Name", eSigIoMask.InputSigOnly); - //Model = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - Model", eSigIoMask.InputSigOnly); - //SerialNumber = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Serial Number", eSigIoMask.InputSigOnly); - //Uptime = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Uptime", eSigIoMask.InputSigOnly); - - Firmware = FusionRoom.CreateOffsetStringSig(61, "Info - Processor - Firmware", eSigIoMask.InputSigOnly); - - for (int i = 0; i < Global.ControlSystem.NumProgramsSupported; i++) - { - var join = 62 + i; - var progNum = i + 1; - Program[i] = FusionRoom.CreateOffsetStringSig((uint)join, string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly); - } - - Firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion; - - //var programs = ProcessorProgReg.GetProcessorProgReg(); - - //for (int i = 1; i < Global.ControlSystem.NumProgramsSupported; i++) - //{ - // var join = 62 + i; - // var progNum = i + 1; - // if (programs[i].Exists) - // Program[i].InputSig.StringValue = programs[i].Name; - //} - - } - - void GetTouchpanelInfo() - { - // TODO Get IP and Project Name from TP - } - - void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) - { - if (args.DeviceOnLine) - { - CrestronEnvironment.Sleep(200); - - // Send Push Notification Action request: - - string requestID = "InitialPushRequest"; - - - string actionRequest = - string.Format("\n{0}\n", requestID) + - "RegisterPushModel\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n"; - - Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); - - FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; - - - // Request current Fusion Server Time - - string timeRequestID = "TimeRequest"; - - string timeRequest = string.Format("{0}", timeRequestID); - - FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest; - } - - } - - /// - /// Generates a room schedule request for this room for the next 24 hours. - /// - /// string identifying this request. Used with a corresponding ScheduleResponse value - public void RequestFullRoomSchedule(object callbackObject) - { - DateTime now = DateTime.Today; - - string currentTime = now.ToString("s"); - - string requestTest = - string.Format("FullSchedleRequest{0}{1}24", RoomGuid, currentTime); - - Debug.Console(2, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest); - - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest; - - if (IsRegisteredForSchedulePushNotifications) - PushNotificationTimer.Stop(); - } - - /// - /// Wrapper method to allow console commands to modify the current meeting end time - /// - /// meetingID extendTime - public void ModifyMeetingEndTimeConsoleHelper(string command) - { - string requestID; - string meetingID = null; - int extendMinutes = -1; - - requestID = "ModifyMeetingTest12345"; - - try - { - var tokens = command.Split(' '); - - meetingID = tokens[0]; - extendMinutes = Int32.Parse(tokens[1]); - - } - catch (Exception e) - { - Debug.Console(1, this, "Error parsing console command: {0}", e); - } - - ModifyMeetingEndTime(requestID, extendMinutes); - - } - - /// - /// Ends or Extends the current meeting by the specified number of minutes. - /// - /// Number of minutes to extend the meeting. A value of 0 will end the meeting. - public void ModifyMeetingEndTime(string requestID, int extendMinutes) - { - if(CurrentMeeting == null) - { - Debug.Console(1, this, "No meeting in progress. Unable to modify end time."); - return; - } - - if (extendMinutes > -1) - { - if(extendMinutes > 0) - { - var extendTime = CurrentMeeting.dtEnd - DateTime.Now; - double extendMinutesRaw = extendTime.TotalMinutes; - - extendMinutes = extendMinutes + (int)Math.Round(extendMinutesRaw); - } - - - string requestTest = string.Format( - "{0}{1}MeetingChange" - , 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"); - } - - - } - - /// - /// Creates and Ad Hoc meeting with a duration of 1 hour, or until the next meeting if in less than 1 hour. - /// - 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 = - "" + - string.Format("{0}", requestID) + - string.Format("{0}", RoomGuid) + - "" + - string.Format("{0}", now.ToString("s")) + - string.Format("{0}", dtEnd.ToString("s")) + - "AdHoc Meeting" + - "Room User" + - "Example Message" + - "" + - ""; - - Debug.Console(2, this, "Sending CreateMeeting Request: \n{0}", createMeetingRequest); - - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = createMeetingRequest; - - //Debug.Console(1, this, "Sending CreateMeeting Request: \n{0}", command); - - //FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = command; - - } - - /// - /// Event handler method for Device Extender sig changes - /// - /// - /// - void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) - { - Debug.Console(2, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); - - - if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQueryResponse) - { - try - { - XmlDocument message = new XmlDocument(); - - message.LoadXml(args.Sig.StringValue); - - var actionResponse = message["ActionResponse"]; - - if (actionResponse != null) - { - var requestID = actionResponse["RequestID"]; - - if (requestID.InnerText == "InitialPushRequest") - { - if (actionResponse["ActionID"].InnerText == "RegisterPushModel") - { - 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) - { - Debug.Console(1, this, "Error parsing ActionQueryResponse: {0}", e); - } - } - else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQueryResponse) - { - try - { - XmlDocument message = new XmlDocument(); - - message.LoadXml(args.Sig.StringValue); - - var localDateTimeResponse = message["LocalTimeResponse"]; - - if (localDateTimeResponse != null) - { - var localDateTime = localDateTimeResponse["LocalDateTime"]; - - if (localDateTime != null) - { - var tempLocalDateTime = localDateTime.InnerText; - - DateTime currentTime = DateTime.Parse(tempLocalDateTime); - - Debug.Console(1, this, "DateTime from Fusion Server: {0}", currentTime); - - // Parse time and date from response and insert values - CrestronEnvironment.SetTimeAndDate((ushort)currentTime.Hour, (ushort)currentTime.Minute, (ushort)currentTime.Second, (ushort)currentTime.Month, (ushort)currentTime.Day, (ushort)currentTime.Year); - - Debug.Console(1, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime()); - } - } - } - catch (Exception e) - { - Debug.Console(1, this, "Error parsing LocalDateTimeQueryResponse: {0}", e); - } - } - } - - /// - /// Event handler method for Device Extender sig changes - /// - /// - /// - void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) - { - Debug.Console(2, 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) - { - try - { - ScheduleResponse scheduleResponse = new ScheduleResponse(); - - XmlDocument message = new XmlDocument(); - - message.LoadXml(args.Sig.StringValue); - - var response = message["ScheduleResponse"]; - - if (response != null) - { - // Check for push notification - if (response["RequestID"].InnerText == "RVRequest") - { - var action = response["Action"]; - - if (action.OuterXml.IndexOf("RequestSchedule") > -1) - { - PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout); - } - } - 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) - { - if (element.Name == "RequestID") - { - scheduleResponse.RequestID = element.InnerText; - } - else if (element.Name == "RoomID") - { - scheduleResponse.RoomID = element.InnerText; - } - else if (element.Name == "RoomName") - { - scheduleResponse.RoomName = element.InnerText; - } - else if (element.Name == "Event") - { - Debug.Console(2, this, "Event Found:\n{0}", element.OuterXml); - - XmlReader reader = new XmlReader(element.OuterXml); - - Event tempEvent = new Event(); - - tempEvent = CrestronXMLSerialization.DeSerializeObject(reader); - - 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); - - // Fire Schedule Change Event - var handler = ScheduleChange; - - if (handler != null) - { - handler(this, new ScheduleChangeEventArgs() { Schedule = CurrentSchedule }); - } - - } - } - - - - } - catch (Exception e) - { - Debug.Console(1, this, "Error parsing ScheduleResponse: {0}", e); - } - } - else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse) - { - Debug.Console(2, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); - } - - } - - /// - /// Prints today's schedule to console for debugging - /// - void PrintTodaysSchedule() - { - if (Debug.Level > 1) - { - if (CurrentSchedule.Meetings.Count > 0) - { - Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name); - - foreach (Event e in CurrentSchedule.Meetings) - { - Debug.Console(1, this, "Subject: {0}", e.Subject); - Debug.Console(1, this, "Organizer: {0}", e.Organizer); - Debug.Console(1, this, "MeetingID: {0}", e.MeetingID); - Debug.Console(1, this, "Start Time: {0}", e.dtStart); - Debug.Console(1, this, "End Time: {0}", e.dtEnd); - Debug.Console(1, this, "Duration: {0}\n", e.DurationInMinutes); - } - } - } - } - - void SetUpSources() - { - // Sources - var dict = ConfigReader.ConfigObject.GetSourceListForKey(Room.SourceListKey); - if (dict != null) - { - // NEW PROCESS: - // Make these lists and insert the fusion attributes by iterating these - var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); - uint i = 1; - foreach (var kvp in setTopBoxes) - { - TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 5) // We only have five spots - break; - } - - var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); - i = 1; - foreach (var kvp in discPlayers) - { - TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 5) // We only have five spots - break; - } - - var laptops = dict.Where(d => d.Value.SourceDevice is Laptop); - i = 1; - foreach (var kvp in laptops) - { - TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 10) // We only have ten spots??? - break; - } - - foreach (var kvp in dict) - { - var usageDevice = kvp.Value.SourceDevice as IUsageTracking; - - if (usageDevice != null) - { - usageDevice.UsageTracker = new UsageTracking(usageDevice as Device); - usageDevice.UsageTracker.UsageIsTracked = true; - usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); - } - } - - } - else - { - Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", - Room.SourceListKey, Room.Key); - } - } - - /// - /// Collects usage data from source and sends to Fusion - /// - /// - /// - void UsageTracker_DeviceUsageEnded(object sender, DeviceUsageEventArgs e) - { - var deviceTracker = sender as UsageTracking; - - var configDevice = ConfigReader.ConfigObject.Devices.Where(d => d.Key.Equals(deviceTracker.Parent)); - - string group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key); - - string currentMeetingId = "-"; - - if (CurrentMeeting != null) - currentMeetingId = CurrentMeeting.MeetingID; - - //String Format: "USAGE||[Date YYYY-MM-DD]||[Time HH-mm-ss]||TIME||[Asset_Type]||[Asset_Name]||[Minutes_used]||[Asset_ID]||[Meeting_ID]" - // [Asset_ID] property does not appear to be used in Crestron SSI examples. They are sending "-" instead so that's what is replicated here - string deviceUsage = string.Format("USAGE||{0}||{1}||TIME||{2}||{3}||-||{4}||-||{5}||{6}||\r\n", e.UsageEndTime.ToString("yyyy-MM-dd"), e.UsageEndTime.ToString("HH:mm:ss"), - group, deviceTracker.Parent.Name, e.MinutesUsed, "-", currentMeetingId); - - Debug.Console(1, this, "Device usage for: {0} ended at {1}. In use for {2} minutes", deviceTracker.Parent.Name, e.UsageEndTime, e.MinutesUsed); - - FusionRoom.DeviceUsage.InputSig.StringValue = deviceUsage; - - Debug.Console(1, this, "Device usage string: {0}", deviceUsage); - } - - - void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc) - { - Debug.Console(2, this, "Creating attribute '{0}' with join {1} for source {2}", - attrName, attrNum, pSrc.Key); - try - { - var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig); - // Need feedback when this source is selected - // Event handler, added below, will compare source changes with this sig dict - SourceToFeedbackSigs.Add(pSrc, sigD.InputSig); - - // And respond to selection in Fusion - sigD.OutputSig.SetSigFalseAction(() => Room.RunRouteAction(routeKey)); - } - catch (Exception) - { - Debug.Console(2, this, "Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING", attrNum, attrName, pSrc.Key); - } - } - - /// - /// - /// - void SetUpCommunitcationMonitors() - { - // Attach to all room's devices with monitors. - //foreach (var dev in DeviceManager.Devices) - foreach (var dev in DeviceManager.GetDevices()) - { - if (!(dev is ICommunicationMonitor)) - continue; - - var keyNum = ExtractNumberFromKey(dev.Key); - if (keyNum == -1) - { - Debug.Console(1, this, "WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes", - dev.Key); - continue; - } - string attrName = null; - uint attrNum = Convert.ToUInt32(keyNum); - - if (dev is EssentialsTouchpanelController) - { - if ((dev as EssentialsTouchpanelController).Panel is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button) - { - if (attrNum > 10) - continue; - attrName = "Online - Touch Panel " + attrNum; - attrNum += 150; - } - else if ((dev as EssentialsTouchpanelController).Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics) - { - if (attrNum > 10) - continue; - attrName = "Online - XPanel " + attrNum; - attrNum += 160; - } - } - - //else - if (dev is DisplayBase) - { - if (attrNum > 10) - continue; - attrName = "Online - Display " + attrNum; - attrNum += 170; - } - //else if (dev is DvdDeviceBase) - //{ - // if (attrNum > 5) - // continue; - // attrName = "Device Ok - DVD " + attrNum; - // attrNum += 260; - //} - // add set top box - - // add Cresnet roll-up - - // add DM-devices roll-up - - if (attrName != null) - { - // Link comm status to sig and update - var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly); - var smd = dev as ICommunicationMonitor; - sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk; - smd.CommunicationMonitor.StatusChange += (o, a) => - { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; }; - Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", dev.Key, attrName); - } - } - } - - void SetUpDisplay() - { - try - { - //Setup Display Usage Monitoring - - var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase); - - // Consider updating this in multiple display systems - - foreach (DisplayBase display in displays) - { - display.UsageTracker = new UsageTracking(display); - display.UsageTracker.UsageIsTracked = true; - display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); - } - - var defaultDisplay = Room.DefaultDisplay as DisplayBase; - if (defaultDisplay == null) - { - Debug.Console(1, this, "Cannot link null display to Fusion"); - return; - } - - var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); }); - var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); }); - - // Display to fusion room sigs - FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; - FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; - defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); - if (defaultDisplay is IDisplayUsage) - (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); - - - - MapDisplayToRoomJoins(1, 158, defaultDisplay); - - - var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); - - //Check for existing asset in GUIDs collection - - var tempAsset = new FusionAsset(); - - if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) - { - tempAsset = FusionStaticAssets[deviceConfig.Uid]; - } - else - { - // Create a new asset - tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", ""); - FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); - } - - var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); - dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; - dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; - defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); - // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); - // Use extension methods - dispAsset.TrySetMakeModel(defaultDisplay); - dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); - } - catch (Exception e) - { - Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); - } - - } - - /// - /// Maps room attributes to a display at a specified index - /// - /// - /// a - void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) - { - string displayName = string.Format("Display {0} - ", displayIndex); - - - if(display == Room.DefaultDisplay) - { - // Display volume - var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); - defaultDisplayVolume.OutputSig.UserObject = new Action(b => (display as IBasicVolumeWithFeedback).SetVolume(b)); - (display as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(defaultDisplayVolume.InputSig); - - // Power on - var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); }); - display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - - // Power Off - var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ; - display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - - // Current Source - var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); - defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) Room.RunRouteAction("roomOff"); }); ; - - //var dict = ConfigReader.ConfigObject.GetSourceListForKey(Room.SourceListKey); - - //foreach (var item in dict) - //{ - // if(item.Key != "roomOff") - // { - // var defaultDisplaySource = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + (uint)item.Value.Order + 9 , string.Format("{0}Source {1}", displayIndex, item.Value.Order), eSigIoMask.InputOutputSig); - // defaultDisplaySource.OutputSig.UserObject = new Action(b => { if (!b) Room.RunRouteAction(item.Key); }); - - // //defaultDisplaySource.InputSig = Source[item.Value.Order].InputSig; - // } - - //} - } - } - - //void Room_CurrentSingleSourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) - //{ - // for (int i = 1; i <= Source.Length; i++) - // { - // Source[i].InputSig.BoolValue = false; - // } - - // Source[info.Order].InputSig.BoolValue = true; - - // // Need to check for current source key against source list and update Source[] BooleanSigData as appropriate - - //} - - void SetUpError() - { - // Roll up ALL device errors - ErrorMessageRollUp = new StatusMonitorCollection(this); - foreach (var dev in DeviceManager.GetDevices()) - { - var md = dev as ICommunicationMonitor; - if (md != null) - { - ErrorMessageRollUp.AddMonitor(md.CommunicationMonitor); - Debug.Console(2, this, "Adding '{0}' to room's overall error monitor", md.CommunicationMonitor.Parent.Key); - } - } - ErrorMessageRollUp.Start(); - FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message; - ErrorMessageRollUp.StatusChange += (o, a) => - { - FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message; - }; - - } - - void SetUpOccupancy() - { - - // Need to have the room occupancy object first and somehow determine the slot number of the Occupancy asset but will not be able to use the UID from config likely. - // Consider defining an object just for Room Occupancy (either eAssetType.Occupancy Sensor (local) or eAssetType.RemoteOccupancySensor (from Fusion sched. panel)) and reserving slot 4 for that asset (statics would start at 5) - - //if (Room.OccupancyObj != null) - //{ - - var tempOccAsset = GUIDs.OccupancyAsset; - - if(tempOccAsset == null) - { - FusionOccSensor = new FusionOccupancySensorAsset(eAssetType.OccupancySensor); - tempOccAsset = FusionOccSensor; - } - - var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name, "Occupancy Sensor", tempOccAsset.InstanceId); - - occSensorAsset.RoomOccupied.AddSigToRVIFile = true; - - var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, "Occ Shutdown - Minutes", eSigIoMask.InputOutputSig); - - // Tie to method on occupancy object - //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b)); - - - // use Room.OccObject.RoomOccupiedFeedback.LinkInputSig(occSensorAsset.InputSig); - //} - } - - /// - /// Helper to get the number from the end of a device's key string - /// - /// -1 if no number matched - int ExtractNumberFromKey(string key) - { - var capture = System.Text.RegularExpressions.Regex.Match(key, @"\D+(\d+)"); - if (!capture.Success) - return -1; - else return Convert.ToInt32(capture.Groups[1].Value); - } - - /// - /// Event handler for when room source changes - /// - void Room_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) - { - // Handle null. Nothing to do when switching from or to null - if (info == null || info.SourceDevice == null) - return; - - var dev = info.SourceDevice; - if (type == ChangeType.WillChange) - { - if (SourceToFeedbackSigs.ContainsKey(dev)) - SourceToFeedbackSigs[dev].BoolValue = false; - } - else - { - if (SourceToFeedbackSigs.ContainsKey(dev)) - SourceToFeedbackSigs[dev].BoolValue = true; - var name = (room == null ? "" : room.Name); - CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name; - } - } - - void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args) - { - - // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors, - // even though they all contain sigs. - - var sigData = (args.UserConfiguredSigDetail as BooleanSigDataFixedName); - if (sigData != null) - { - var outSig = sigData.OutputSig; - if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.BoolValue); - else if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.UShortValue); - else if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.StringValue); - return; - } - - var attrData = (args.UserConfiguredSigDetail as BooleanSigData); - if (attrData != null) - { - var outSig = attrData.OutputSig; - if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.BoolValue); - else if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.UShortValue); - else if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.StringValue); - return; - } - - } - } - - - public static class FusionRoomExtensions - { - /// - /// Creates and returns a fusion attribute. The join number will match the established Simpl - /// standard of 50+, and will generate a 50+ join in the RVI. It calls - /// FusionRoom.AddSig with join number - 49 - /// - /// The new attribute - public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) - { - if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); - number -= 49; - fr.AddSig(eSigType.Bool, number, name, mask); - return fr.UserDefinedBooleanSigDetails[number]; - } - - /// - /// Creates and returns a fusion attribute. The join number will match the established Simpl - /// standard of 50+, and will generate a 50+ join in the RVI. It calls - /// FusionRoom.AddSig with join number - 49 - /// - /// The new attribute - public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) - { - if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); - number -= 49; - fr.AddSig(eSigType.UShort, number, name, mask); - return fr.UserDefinedUShortSigDetails[number]; - } - - /// - /// Creates and returns a fusion attribute. The join number will match the established Simpl - /// standard of 50+, and will generate a 50+ join in the RVI. It calls - /// FusionRoom.AddSig with join number - 49 - /// - /// The new attribute - public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) - { - if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); - number -= 49; - fr.AddSig(eSigType.String, number, name, mask); - return fr.UserDefinedStringSigDetails[number]; - } - - /// - /// Creates and returns a static asset - /// - /// the new asset - public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type, string instanceId) - { - Debug.Console(0, "Adding Fusion Static Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId); - - fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId); - return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset; - } - - public static FusionOccupancySensor CreateOccupancySensorAsset(this FusionRoom fr, uint number, string name, string type, string instanceId) - { - Debug.Console(0, "Adding Fusion Occupancy Sensor Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId); - - fr.AddAsset(eAssetType.OccupancySensor, number, name, type, instanceId); - return fr.UserConfigurableAssetDetails[number].Asset as FusionOccupancySensor; - } - } - - //************************************************************************************************ - /// - /// Extensions to enhance Fusion room, asset and signal creation. - /// - public static class FusionStaticAssetExtensions - { - /// - /// Tries to set a Fusion asset with the make and model of a device. - /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset. - /// Otherwise, does nothing. - /// - public static void TrySetMakeModel(this FusionStaticAsset asset, Device device) - { - var mm = device as IMakeModel; - if (mm != null) - { - asset.ParamMake.Value = mm.DeviceMake; - asset.ParamModel.Value = mm.DeviceModel; - } - } - - /// - /// Tries to attach the AssetError input on a Fusion asset to a Device's - /// CommunicationMonitor.StatusChange event. Does nothing if the device is not - /// IStatusMonitor - /// - /// - /// - public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device) - { - if (device is ICommunicationMonitor) - { - var monitor = (device as ICommunicationMonitor).CommunicationMonitor; - monitor.StatusChange += (o, a) => - { - // Link connected and error inputs on asset - asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; - asset.AssetError.InputSig.StringValue = a.Status.ToString(); - }; - // set current value - asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk; - asset.AssetError.InputSig.StringValue = monitor.Status.ToString(); - } - } - } - - +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.CrestronXml; +using Crestron.SimplSharp.CrestronXml.Serialization; +using Crestron.SimplSharp.CrestronXmlLinq; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.Fusion; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Core; +using PepperDash.Essentials; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common; + + + +namespace PepperDash.Essentials.Fusion +{ + public class EssentialsHuddleSpaceFusionSystemController : Device + { + public event EventHandler ScheduleChange; + public event EventHandler MeetingEndWarning; + public event EventHandler NextMeetingBeginWarning; + + FusionRoom FusionRoom; + EssentialsHuddleSpaceRoom Room; + Dictionary SourceToFeedbackSigs = + new Dictionary(); + + //BooleanSigData OccupancyStatusSig; + + StatusMonitorCollection ErrorMessageRollUp; + + StringSigData CurrentRoomSourceNameSig; + + #region System Info Sigs + //StringSigData SystemName; + //StringSigData Model; + //StringSigData SerialNumber; + //StringSigData Uptime; + #endregion + + + #region Processor Info Sigs + StringSigData Ip1; + StringSigData Ip2; + StringSigData Gateway; + StringSigData Hostname; + StringSigData Domain; + StringSigData Dns1; + StringSigData Dns2; + StringSigData Mac1; + StringSigData Mac2; + StringSigData NetMask1; + StringSigData NetMask2; + StringSigData Firmware; + + StringSigData[] Program = new StringSigData[10]; + #endregion + + #region Default Display Source Sigs + + BooleanSigData[] Source = new BooleanSigData[10]; + + #endregion + + RoomSchedule CurrentSchedule; + + Event NextMeeting; + + Event CurrentMeeting; + + string RoomGuid + { + get + { + return GUIDs.RoomGuid; + } + + } + + uint IpId; + + FusionRoomGuids GUIDs; + + bool GuidFileExists; + + bool IsRegisteredForSchedulePushNotifications = false; + + CTimer PollTimer = null; + + CTimer PushNotificationTimer = null; + + // Default poll time is 5 min unless overridden by config value + public long SchedulePollInterval = 300000; + + public long PushNotificationTimeout = 5000; + + Dictionary FusionStaticAssets; + + FusionOccupancySensorAsset FusionOccSensor; + + //ScheduleResponseEvent NextMeeting; + + public EssentialsHuddleSpaceFusionSystemController(EssentialsHuddleSpaceRoom room, uint ipId) + : base(room.Key + "-fusion") + { + + Room = room; + + IpId = ipId; + + FusionStaticAssets = new Dictionary(); + + GUIDs = new FusionRoomGuids(); + + var mac = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); + + var slot = Global.ControlSystem.ProgramNumber; + + string guidFilePath = string.Format(@"\NVRAM\Program{0}\{1}-FusionGuids.json", Global.ControlSystem.ProgramNumber, InitialParametersClass.ProgramIDTag); + + GuidFileExists = File.Exists(guidFilePath); + + if (GuidFileExists) + { + ReadGuidFile(guidFilePath); + } + else + { + GUIDs = new FusionRoomGuids(Room.Name, ipId, GUIDs.GenerateNewRoomGuid(slot, mac), FusionStaticAssets); + } + + CreateSymbolAndBasicSigs(IpId); + SetUpSources(); + SetUpCommunitcationMonitors(); + SetUpDisplay(); + SetUpError(); + //SetUpOccupancy(); + + // Make it so! + FusionRVI.GenerateFileForAllFusionDevices(); + + GenerateGuidFile(guidFilePath); + } + + /// + /// Generates the guid file in NVRAM. If the file already exists it will be overwritten. + /// + /// path for the file + void GenerateGuidFile(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + { + Debug.Console(0, this, "Error writing guid file. No path specified."); + return; + } + + CCriticalSection _fileLock = new CCriticalSection(); + + try + { + if (_fileLock == null || _fileLock.Disposed) + return; + + _fileLock.Enter(); + + Debug.Console(1, this, "Writing GUIDs to file"); + + if (FusionOccSensor == null) + GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets); + else + GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets, FusionOccSensor); + + var JSON = JsonConvert.SerializeObject(GUIDs, Newtonsoft.Json.Formatting.Indented); + + using (StreamWriter sw = new StreamWriter(filePath)) + { + sw.Write(JSON); + sw.Flush(); + } + + Debug.Console(1, this, "Guids successfully written to file '{0}'", filePath); + + } + catch (Exception e) + { + Debug.Console(0, this, "Error writing guid file: {0}", e); + } + finally + { + if (_fileLock != null && !_fileLock.Disposed) + _fileLock.Leave(); + } + } + + /// + /// Reads the guid file from NVRAM + /// + /// path for te file + void ReadGuidFile(string filePath) + { + if(string.IsNullOrEmpty(filePath)) + { + Debug.Console(0, this, "Error reading guid file. No path specified."); + return; + } + + CCriticalSection _fileLock = new CCriticalSection(); + + try + { + if(_fileLock == null || _fileLock.Disposed) + return; + + _fileLock.Enter(); + + if(File.Exists(filePath)) + { + var JSON = File.ReadToEnd(filePath, Encoding.ASCII); + + GUIDs = JsonConvert.DeserializeObject(JSON); + + IpId = GUIDs.IpId; + + FusionStaticAssets = GUIDs.StaticAssets; + + } + + Debug.Console(0, this, "Fusion Guids successfully read from file:"); + + Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, IpId, RoomGuid); + + foreach (KeyValuePair item in FusionStaticAssets) + { + Debug.Console(1, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name, item.Value.SlotNumber, item.Value.InstanceId); + } + } + catch (Exception e) + { + Debug.Console(0, this, "Error reading guid file: {0}", e); + } + finally + { + if(_fileLock != null && !_fileLock.Disposed) + _fileLock.Leave(); + } + + } + + void CreateSymbolAndBasicSigs(uint ipId) + { + Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); + + FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); + FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); + + FusionRoom.Register(); + + FusionRoom.FusionStateChange += new FusionStateEventHandler(FusionRoom_FusionStateChange); + + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(FusionRoomSchedule_DeviceExtenderSigChange); + FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange); + FusionRoom.OnlineStatusChange += new OnlineStatusChangeEventHandler(FusionRoom_OnlineStatusChange); + + 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(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); + + // Room to fusion room + Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); + + // Moved to + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly); + // Don't think we need to get current status of this as nothing should be alive yet. + Room.CurrentSingleSourceChange += new SourceInfoChangeHandler(Room_CurrentSourceInfoChange); + + + FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction(Room.PowerOnToDefaultOrLastSource); + FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => Room.RunRouteAction("roomOff")); + // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); + FusionRoom.ErrorMessage.InputSig.StringValue = + "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;"; + + GetProcessorEthernetValues(); + + GetSystemInfo(); + + GetProcessorInfo(); + + CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); + } + + void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) + { + if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp) + { + GetProcessorEthernetValues(); + } + } + + void GetSystemInfo() + { + //SystemName.InputSig.StringValue = Room.Name; + //Model.InputSig.StringValue = InitialParametersClass.ControllerPromptName; + //SerialNumber.InputSig.StringValue = InitialParametersClass. + + string response = string.Empty; + + var systemReboot = FusionRoom.CreateOffsetBoolSig(74, "Processor - Reboot", eSigIoMask.OutputSigOnly); + systemReboot.OutputSig.SetSigFalseAction(() => CrestronConsole.SendControlSystemCommand("reboot", ref response)); + } + + void GetProcessorEthernetValues() + { + Ip1 = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - IP 1", eSigIoMask.InputSigOnly); + Ip2 = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - IP 2", eSigIoMask.InputSigOnly); + Gateway = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Gateway", eSigIoMask.InputSigOnly); + Hostname = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Hostname", eSigIoMask.InputSigOnly); + Domain = FusionRoom.CreateOffsetStringSig(54, "Info - Processor - Domain", eSigIoMask.InputSigOnly); + Dns1 = FusionRoom.CreateOffsetStringSig(55, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly); + Dns2 = FusionRoom.CreateOffsetStringSig(56, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly); + Mac1 = FusionRoom.CreateOffsetStringSig(57, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly); + Mac2 = FusionRoom.CreateOffsetStringSig(58, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly); + NetMask1 = FusionRoom.CreateOffsetStringSig(59, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly); + NetMask2 = FusionRoom.CreateOffsetStringSig(60, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly); + + // Interface =0 + Ip1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); + Gateway.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0); + Hostname.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); + Domain.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); + + var dnsServers = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, 0).Split(','); + Dns1.InputSig.StringValue = dnsServers[0]; + if (dnsServers.Length > 1) + Dns2.InputSig.StringValue = dnsServers[1]; + + Mac1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); + NetMask1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0); + + // Interface 1 + + if (InitialParametersClass.NumberOfEthernetInterfaces > 1) // Only get these values if the processor has more than 1 NIC + { + Ip2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1); + Mac2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 1); + NetMask2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 1); + } + } + + void GetProcessorInfo() + { + //SystemName = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - System Name", eSigIoMask.InputSigOnly); + //Model = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - Model", eSigIoMask.InputSigOnly); + //SerialNumber = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Serial Number", eSigIoMask.InputSigOnly); + //Uptime = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Uptime", eSigIoMask.InputSigOnly); + + Firmware = FusionRoom.CreateOffsetStringSig(61, "Info - Processor - Firmware", eSigIoMask.InputSigOnly); + + for (int i = 0; i < Global.ControlSystem.NumProgramsSupported; i++) + { + var join = 62 + i; + var progNum = i + 1; + Program[i] = FusionRoom.CreateOffsetStringSig((uint)join, string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly); + } + + Firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion; + + //var programs = ProcessorProgReg.GetProcessorProgReg(); + + //for (int i = 1; i < Global.ControlSystem.NumProgramsSupported; i++) + //{ + // var join = 62 + i; + // var progNum = i + 1; + // if (programs[i].Exists) + // Program[i].InputSig.StringValue = programs[i].Name; + //} + + } + + void GetTouchpanelInfo() + { + // TODO Get IP and Project Name from TP + } + + void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) + { + if (args.DeviceOnLine) + { + CrestronEnvironment.Sleep(200); + + // Send Push Notification Action request: + + string requestID = "InitialPushRequest"; + + + string actionRequest = + string.Format("\n{0}\n", requestID) + + "RegisterPushModel\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + + Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); + + FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; + + + // Request current Fusion Server Time + + string timeRequestID = "TimeRequest"; + + string timeRequest = string.Format("{0}", timeRequestID); + + FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest; + } + + } + + /// + /// Generates a room schedule request for this room for the next 24 hours. + /// + /// string identifying this request. Used with a corresponding ScheduleResponse value + public void RequestFullRoomSchedule(object callbackObject) + { + DateTime now = DateTime.Today; + + string currentTime = now.ToString("s"); + + string requestTest = + string.Format("FullSchedleRequest{0}{1}24", RoomGuid, currentTime); + + Debug.Console(2, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest); + + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest; + + if (IsRegisteredForSchedulePushNotifications) + PushNotificationTimer.Stop(); + } + + /// + /// Wrapper method to allow console commands to modify the current meeting end time + /// + /// meetingID extendTime + public void ModifyMeetingEndTimeConsoleHelper(string command) + { + string requestID; + string meetingID = null; + int extendMinutes = -1; + + requestID = "ModifyMeetingTest12345"; + + try + { + var tokens = command.Split(' '); + + meetingID = tokens[0]; + extendMinutes = Int32.Parse(tokens[1]); + + } + catch (Exception e) + { + Debug.Console(1, this, "Error parsing console command: {0}", e); + } + + ModifyMeetingEndTime(requestID, extendMinutes); + + } + + /// + /// Ends or Extends the current meeting by the specified number of minutes. + /// + /// Number of minutes to extend the meeting. A value of 0 will end the meeting. + public void ModifyMeetingEndTime(string requestID, int extendMinutes) + { + if(CurrentMeeting == null) + { + Debug.Console(1, this, "No meeting in progress. Unable to modify end time."); + return; + } + + if (extendMinutes > -1) + { + if(extendMinutes > 0) + { + var extendTime = CurrentMeeting.dtEnd - DateTime.Now; + double extendMinutesRaw = extendTime.TotalMinutes; + + extendMinutes = extendMinutes + (int)Math.Round(extendMinutesRaw); + } + + + string requestTest = string.Format( + "{0}{1}MeetingChange" + , 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"); + } + + + } + + /// + /// Creates and Ad Hoc meeting with a duration of 1 hour, or until the next meeting if in less than 1 hour. + /// + 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 = + "" + + string.Format("{0}", requestID) + + string.Format("{0}", RoomGuid) + + "" + + string.Format("{0}", now.ToString("s")) + + string.Format("{0}", dtEnd.ToString("s")) + + "AdHoc Meeting" + + "Room User" + + "Example Message" + + "" + + ""; + + Debug.Console(2, this, "Sending CreateMeeting Request: \n{0}", createMeetingRequest); + + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = createMeetingRequest; + + //Debug.Console(1, this, "Sending CreateMeeting Request: \n{0}", command); + + //FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = command; + + } + + /// + /// Event handler method for Device Extender sig changes + /// + /// + /// + void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) + { + Debug.Console(2, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); + + + if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQueryResponse) + { + try + { + XmlDocument message = new XmlDocument(); + + message.LoadXml(args.Sig.StringValue); + + var actionResponse = message["ActionResponse"]; + + if (actionResponse != null) + { + var requestID = actionResponse["RequestID"]; + + if (requestID.InnerText == "InitialPushRequest") + { + if (actionResponse["ActionID"].InnerText == "RegisterPushModel") + { + 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) + { + Debug.Console(1, this, "Error parsing ActionQueryResponse: {0}", e); + } + } + else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQueryResponse) + { + try + { + XmlDocument message = new XmlDocument(); + + message.LoadXml(args.Sig.StringValue); + + var localDateTimeResponse = message["LocalTimeResponse"]; + + if (localDateTimeResponse != null) + { + var localDateTime = localDateTimeResponse["LocalDateTime"]; + + if (localDateTime != null) + { + var tempLocalDateTime = localDateTime.InnerText; + + DateTime currentTime = DateTime.Parse(tempLocalDateTime); + + Debug.Console(1, this, "DateTime from Fusion Server: {0}", currentTime); + + // Parse time and date from response and insert values + CrestronEnvironment.SetTimeAndDate((ushort)currentTime.Hour, (ushort)currentTime.Minute, (ushort)currentTime.Second, (ushort)currentTime.Month, (ushort)currentTime.Day, (ushort)currentTime.Year); + + Debug.Console(1, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime()); + } + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error parsing LocalDateTimeQueryResponse: {0}", e); + } + } + } + + /// + /// Event handler method for Device Extender sig changes + /// + /// + /// + void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) + { + Debug.Console(2, 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) + { + try + { + ScheduleResponse scheduleResponse = new ScheduleResponse(); + + XmlDocument message = new XmlDocument(); + + message.LoadXml(args.Sig.StringValue); + + var response = message["ScheduleResponse"]; + + if (response != null) + { + // Check for push notification + if (response["RequestID"].InnerText == "RVRequest") + { + var action = response["Action"]; + + if (action.OuterXml.IndexOf("RequestSchedule") > -1) + { + PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout); + } + } + 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) + { + if (element.Name == "RequestID") + { + scheduleResponse.RequestID = element.InnerText; + } + else if (element.Name == "RoomID") + { + scheduleResponse.RoomID = element.InnerText; + } + else if (element.Name == "RoomName") + { + scheduleResponse.RoomName = element.InnerText; + } + else if (element.Name == "Event") + { + Debug.Console(2, this, "Event Found:\n{0}", element.OuterXml); + + XmlReader reader = new XmlReader(element.OuterXml); + + Event tempEvent = new Event(); + + tempEvent = CrestronXMLSerialization.DeSerializeObject(reader); + + 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); + + // Fire Schedule Change Event + var handler = ScheduleChange; + + if (handler != null) + { + handler(this, new ScheduleChangeEventArgs() { Schedule = CurrentSchedule }); + } + + } + } + + + + } + catch (Exception e) + { + Debug.Console(1, this, "Error parsing ScheduleResponse: {0}", e); + } + } + else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse) + { + Debug.Console(2, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); + } + + } + + /// + /// Prints today's schedule to console for debugging + /// + void PrintTodaysSchedule() + { + if (Debug.Level > 1) + { + if (CurrentSchedule.Meetings.Count > 0) + { + Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name); + + foreach (Event e in CurrentSchedule.Meetings) + { + Debug.Console(1, this, "Subject: {0}", e.Subject); + Debug.Console(1, this, "Organizer: {0}", e.Organizer); + Debug.Console(1, this, "MeetingID: {0}", e.MeetingID); + Debug.Console(1, this, "Start Time: {0}", e.dtStart); + Debug.Console(1, this, "End Time: {0}", e.dtEnd); + Debug.Console(1, this, "Duration: {0}\n", e.DurationInMinutes); + } + } + } + } + + void SetUpSources() + { + // Sources + var dict = ConfigReader.ConfigObject.GetSourceListForKey(Room.SourceListKey); + if (dict != null) + { + // NEW PROCESS: + // Make these lists and insert the fusion attributes by iterating these + var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); + uint i = 1; + foreach (var kvp in setTopBoxes) + { + TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 5) // We only have five spots + break; + } + + var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); + i = 1; + foreach (var kvp in discPlayers) + { + TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 5) // We only have five spots + break; + } + + var laptops = dict.Where(d => d.Value.SourceDevice is Laptop); + i = 1; + foreach (var kvp in laptops) + { + TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 10) // We only have ten spots??? + break; + } + + foreach (var kvp in dict) + { + var usageDevice = kvp.Value.SourceDevice as IUsageTracking; + + if (usageDevice != null) + { + usageDevice.UsageTracker = new UsageTracking(usageDevice as Device); + usageDevice.UsageTracker.UsageIsTracked = true; + usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + } + } + + } + else + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", + Room.SourceListKey, Room.Key); + } + } + + /// + /// Collects usage data from source and sends to Fusion + /// + /// + /// + void UsageTracker_DeviceUsageEnded(object sender, DeviceUsageEventArgs e) + { + var deviceTracker = sender as UsageTracking; + + var configDevice = ConfigReader.ConfigObject.Devices.Where(d => d.Key.Equals(deviceTracker.Parent)); + + string group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key); + + string currentMeetingId = "-"; + + if (CurrentMeeting != null) + currentMeetingId = CurrentMeeting.MeetingID; + + //String Format: "USAGE||[Date YYYY-MM-DD]||[Time HH-mm-ss]||TIME||[Asset_Type]||[Asset_Name]||[Minutes_used]||[Asset_ID]||[Meeting_ID]" + // [Asset_ID] property does not appear to be used in Crestron SSI examples. They are sending "-" instead so that's what is replicated here + string deviceUsage = string.Format("USAGE||{0}||{1}||TIME||{2}||{3}||-||{4}||-||{5}||{6}||\r\n", e.UsageEndTime.ToString("yyyy-MM-dd"), e.UsageEndTime.ToString("HH:mm:ss"), + group, deviceTracker.Parent.Name, e.MinutesUsed, "-", currentMeetingId); + + Debug.Console(1, this, "Device usage for: {0} ended at {1}. In use for {2} minutes", deviceTracker.Parent.Name, e.UsageEndTime, e.MinutesUsed); + + FusionRoom.DeviceUsage.InputSig.StringValue = deviceUsage; + + Debug.Console(1, this, "Device usage string: {0}", deviceUsage); + } + + + void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc) + { + Debug.Console(2, this, "Creating attribute '{0}' with join {1} for source {2}", + attrName, attrNum, pSrc.Key); + try + { + var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig); + // Need feedback when this source is selected + // Event handler, added below, will compare source changes with this sig dict + SourceToFeedbackSigs.Add(pSrc, sigD.InputSig); + + // And respond to selection in Fusion + sigD.OutputSig.SetSigFalseAction(() => Room.RunRouteAction(routeKey)); + } + catch (Exception) + { + Debug.Console(2, this, "Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING", attrNum, attrName, pSrc.Key); + } + } + + /// + /// + /// + void SetUpCommunitcationMonitors() + { + // Attach to all room's devices with monitors. + //foreach (var dev in DeviceManager.Devices) + foreach (var dev in DeviceManager.GetDevices()) + { + if (!(dev is ICommunicationMonitor)) + continue; + + var keyNum = ExtractNumberFromKey(dev.Key); + if (keyNum == -1) + { + Debug.Console(1, this, "WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes", + dev.Key); + continue; + } + string attrName = null; + uint attrNum = Convert.ToUInt32(keyNum); + + if (dev is EssentialsTouchpanelController) + { + if ((dev as EssentialsTouchpanelController).Panel is Crestron.SimplSharpPro.DeviceSupport.TswFt5Button) + { + if (attrNum > 10) + continue; + attrName = "Online - Touch Panel " + attrNum; + attrNum += 150; + } + else if ((dev as EssentialsTouchpanelController).Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics) + { + if (attrNum > 10) + continue; + attrName = "Online - XPanel " + attrNum; + attrNum += 160; + } + } + + //else + if (dev is DisplayBase) + { + if (attrNum > 10) + continue; + attrName = "Online - Display " + attrNum; + attrNum += 170; + } + //else if (dev is DvdDeviceBase) + //{ + // if (attrNum > 5) + // continue; + // attrName = "Device Ok - DVD " + attrNum; + // attrNum += 260; + //} + // add set top box + + // add Cresnet roll-up + + // add DM-devices roll-up + + if (attrName != null) + { + // Link comm status to sig and update + var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly); + var smd = dev as ICommunicationMonitor; + sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk; + smd.CommunicationMonitor.StatusChange += (o, a) => + { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; }; + Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", dev.Key, attrName); + } + } + } + + void SetUpDisplay() + { + try + { + //Setup Display Usage Monitoring + + var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase); + + // Consider updating this in multiple display systems + + foreach (DisplayBase display in displays) + { + display.UsageTracker = new UsageTracking(display); + display.UsageTracker.UsageIsTracked = true; + display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + } + + var defaultDisplay = Room.DefaultDisplay as DisplayBase; + if (defaultDisplay == null) + { + Debug.Console(1, this, "Cannot link null display to Fusion"); + return; + } + + var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); }); + var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); }); + + // Display to fusion room sigs + FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; + FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; + defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); + if (defaultDisplay is IDisplayUsage) + (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); + + + + MapDisplayToRoomJoins(1, 158, defaultDisplay); + + + var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); + + //Check for existing asset in GUIDs collection + + var tempAsset = new FusionAsset(); + + if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) + { + tempAsset = FusionStaticAssets[deviceConfig.Uid]; + } + else + { + // Create a new asset + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", ""); + FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); + } + + var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; + dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; + defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); + // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); + // Use extension methods + dispAsset.TrySetMakeModel(defaultDisplay); + dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); + } + catch (Exception e) + { + Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); + } + + } + + /// + /// Maps room attributes to a display at a specified index + /// + /// + /// a + void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) + { + string displayName = string.Format("Display {0} - ", displayIndex); + + + if(display == Room.DefaultDisplay) + { + // Display volume + var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); + defaultDisplayVolume.OutputSig.UserObject = new Action(b => (display as IBasicVolumeWithFeedback).SetVolume(b)); + (display as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(defaultDisplayVolume.InputSig); + + // Power on + var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); }); + display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + + // Power Off + var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ; + display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + + // Current Source + var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); + defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) Room.RunRouteAction("roomOff"); }); ; + + //var dict = ConfigReader.ConfigObject.GetSourceListForKey(Room.SourceListKey); + + //foreach (var item in dict) + //{ + // if(item.Key != "roomOff") + // { + // var defaultDisplaySource = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + (uint)item.Value.Order + 9 , string.Format("{0}Source {1}", displayIndex, item.Value.Order), eSigIoMask.InputOutputSig); + // defaultDisplaySource.OutputSig.UserObject = new Action(b => { if (!b) Room.RunRouteAction(item.Key); }); + + // //defaultDisplaySource.InputSig = Source[item.Value.Order].InputSig; + // } + + //} + } + } + + //void Room_CurrentSingleSourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) + //{ + // for (int i = 1; i <= Source.Length; i++) + // { + // Source[i].InputSig.BoolValue = false; + // } + + // Source[info.Order].InputSig.BoolValue = true; + + // // Need to check for current source key against source list and update Source[] BooleanSigData as appropriate + + //} + + void SetUpError() + { + // Roll up ALL device errors + ErrorMessageRollUp = new StatusMonitorCollection(this); + foreach (var dev in DeviceManager.GetDevices()) + { + var md = dev as ICommunicationMonitor; + if (md != null) + { + ErrorMessageRollUp.AddMonitor(md.CommunicationMonitor); + Debug.Console(2, this, "Adding '{0}' to room's overall error monitor", md.CommunicationMonitor.Parent.Key); + } + } + ErrorMessageRollUp.Start(); + FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message; + ErrorMessageRollUp.StatusChange += (o, a) => + { + FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message; + }; + + } + + void SetUpOccupancy() + { + + // Need to have the room occupancy object first and somehow determine the slot number of the Occupancy asset but will not be able to use the UID from config likely. + // Consider defining an object just for Room Occupancy (either eAssetType.Occupancy Sensor (local) or eAssetType.RemoteOccupancySensor (from Fusion sched. panel)) and reserving slot 4 for that asset (statics would start at 5) + + //if (Room.OccupancyObj != null) + //{ + + var tempOccAsset = GUIDs.OccupancyAsset; + + if(tempOccAsset == null) + { + FusionOccSensor = new FusionOccupancySensorAsset(eAssetType.OccupancySensor); + tempOccAsset = FusionOccSensor; + } + + var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name, "Occupancy Sensor", tempOccAsset.InstanceId); + + occSensorAsset.RoomOccupied.AddSigToRVIFile = true; + + var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, "Occ Shutdown - Minutes", eSigIoMask.InputOutputSig); + + // Tie to method on occupancy object + //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b)); + + + // use Room.OccObject.RoomOccupiedFeedback.LinkInputSig(occSensorAsset.InputSig); + //} + } + + /// + /// Helper to get the number from the end of a device's key string + /// + /// -1 if no number matched + int ExtractNumberFromKey(string key) + { + var capture = System.Text.RegularExpressions.Regex.Match(key, @"\D+(\d+)"); + if (!capture.Success) + return -1; + else return Convert.ToInt32(capture.Groups[1].Value); + } + + /// + /// Event handler for when room source changes + /// + void Room_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) + { + // Handle null. Nothing to do when switching from or to null + if (info == null || info.SourceDevice == null) + return; + + var dev = info.SourceDevice; + if (type == ChangeType.WillChange) + { + if (SourceToFeedbackSigs.ContainsKey(dev)) + SourceToFeedbackSigs[dev].BoolValue = false; + } + else + { + if (SourceToFeedbackSigs.ContainsKey(dev)) + SourceToFeedbackSigs[dev].BoolValue = true; + var name = (room == null ? "" : room.Name); + CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name; + } + } + + void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args) + { + + // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors, + // even though they all contain sigs. + + var sigData = (args.UserConfiguredSigDetail as BooleanSigDataFixedName); + if (sigData != null) + { + var outSig = sigData.OutputSig; + if (outSig.UserObject is Action) + (outSig.UserObject as Action).Invoke(outSig.BoolValue); + else if (outSig.UserObject is Action) + (outSig.UserObject as Action).Invoke(outSig.UShortValue); + else if (outSig.UserObject is Action) + (outSig.UserObject as Action).Invoke(outSig.StringValue); + return; + } + + var attrData = (args.UserConfiguredSigDetail as BooleanSigData); + if (attrData != null) + { + var outSig = attrData.OutputSig; + if (outSig.UserObject is Action) + (outSig.UserObject as Action).Invoke(outSig.BoolValue); + else if (outSig.UserObject is Action) + (outSig.UserObject as Action).Invoke(outSig.UShortValue); + else if (outSig.UserObject is Action) + (outSig.UserObject as Action).Invoke(outSig.StringValue); + return; + } + + } + } + + + public static class FusionRoomExtensions + { + /// + /// Creates and returns a fusion attribute. The join number will match the established Simpl + /// standard of 50+, and will generate a 50+ join in the RVI. It calls + /// FusionRoom.AddSig with join number - 49 + /// + /// The new attribute + public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) + { + if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); + number -= 49; + fr.AddSig(eSigType.Bool, number, name, mask); + return fr.UserDefinedBooleanSigDetails[number]; + } + + /// + /// Creates and returns a fusion attribute. The join number will match the established Simpl + /// standard of 50+, and will generate a 50+ join in the RVI. It calls + /// FusionRoom.AddSig with join number - 49 + /// + /// The new attribute + public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) + { + if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); + number -= 49; + fr.AddSig(eSigType.UShort, number, name, mask); + return fr.UserDefinedUShortSigDetails[number]; + } + + /// + /// Creates and returns a fusion attribute. The join number will match the established Simpl + /// standard of 50+, and will generate a 50+ join in the RVI. It calls + /// FusionRoom.AddSig with join number - 49 + /// + /// The new attribute + public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) + { + if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); + number -= 49; + fr.AddSig(eSigType.String, number, name, mask); + return fr.UserDefinedStringSigDetails[number]; + } + + /// + /// Creates and returns a static asset + /// + /// the new asset + public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type, string instanceId) + { + Debug.Console(0, "Adding Fusion Static Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId); + + fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId); + return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset; + } + + public static FusionOccupancySensor CreateOccupancySensorAsset(this FusionRoom fr, uint number, string name, string type, string instanceId) + { + Debug.Console(0, "Adding Fusion Occupancy Sensor Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId); + + fr.AddAsset(eAssetType.OccupancySensor, number, name, type, instanceId); + return fr.UserConfigurableAssetDetails[number].Asset as FusionOccupancySensor; + } + } + + //************************************************************************************************ + /// + /// Extensions to enhance Fusion room, asset and signal creation. + /// + public static class FusionStaticAssetExtensions + { + /// + /// Tries to set a Fusion asset with the make and model of a device. + /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset. + /// Otherwise, does nothing. + /// + public static void TrySetMakeModel(this FusionStaticAsset asset, Device device) + { + var mm = device as IMakeModel; + if (mm != null) + { + asset.ParamMake.Value = mm.DeviceMake; + asset.ParamModel.Value = mm.DeviceModel; + } + } + + /// + /// Tries to attach the AssetError input on a Fusion asset to a Device's + /// CommunicationMonitor.StatusChange event. Does nothing if the device is not + /// IStatusMonitor + /// + /// + /// + public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device) + { + if (device is ICommunicationMonitor) + { + var monitor = (device as ICommunicationMonitor).CommunicationMonitor; + monitor.StatusChange += (o, a) => + { + // Link connected and error inputs on asset + asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; + asset.AssetError.InputSig.StringValue = a.Status.ToString(); + }; + // set current value + asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk; + asset.AssetError.InputSig.StringValue = monitor.Status.ToString(); + } + } + } + + } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs.orig b/Essentials/PepperDashEssentials/OTHER/Fusion/FusionSystemController.cs.orig similarity index 100% rename from Essentials/PepperDashEssentials/Fusion/FusionSystemController.cs.orig rename to Essentials/PepperDashEssentials/OTHER/Fusion/FusionSystemController.cs.orig diff --git a/Essentials/PepperDashEssentials/PepperDashEssentials.csproj b/Essentials/PepperDashEssentials/PepperDashEssentials.csproj index cafe9b6b..800d772c 100644 --- a/Essentials/PepperDashEssentials/PepperDashEssentials.csproj +++ b/Essentials/PepperDashEssentials/PepperDashEssentials.csproj @@ -134,11 +134,11 @@ - - - + + + - + @@ -165,7 +165,7 @@ - + @@ -193,9 +193,6 @@ - - - diff --git a/Essentials/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs b/Essentials/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs index 1d9051d5..3f0b8b77 100644 --- a/Essentials/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs +++ b/Essentials/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs @@ -13,5 +13,6 @@ namespace PepperDash.Essentials.Room.Config public string DefaultAudioKey { get; set; } public string SourceListKey { get; set; } public string DefaultSourceItem { get; set; } + public string VideoCodecKey { get; set; } } } \ No newline at end of file diff --git a/Essentials/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/Essentials/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs index 7761d367..e2b3f5d0 100644 --- a/Essentials/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs +++ b/Essentials/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs @@ -59,7 +59,11 @@ namespace PepperDash.Essentials.Room.Config var props = JsonConvert.DeserializeObject (this.Properties.ToString()); var disp = DeviceManager.GetDeviceForKey(props.DefaultDisplayKey) as IRoutingSinkWithSwitching; - var rm = new EssentialsHuddleVtc1Room(Key, Name, disp, disp, props); + + var codec = DeviceManager.GetDeviceForKey(props.VideoCodecKey) as + PepperDash.Essentials.Devices.Common.VideoCodec.VideoCodecBase; + + var rm = new EssentialsHuddleVtc1Room(Key, Name, disp, disp, codec, props); rm.LogoUrl = props.Logo.GetUrl(); rm.SourceListKey = props.SourceListKey; rm.DefaultSourceItem = props.DefaultSourceItem; diff --git a/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index 60bed37e..d875d674 100644 --- a/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/Essentials/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -7,6 +7,7 @@ using Crestron.SimplSharp; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Room.Config; +using PepperDash.Essentials.Devices.Common.VideoCodec; namespace PepperDash.Essentials { @@ -71,6 +72,8 @@ namespace PepperDash.Essentials public IRoutingSinkNoSwitching DefaultAudioDevice { get; private set; } public IBasicVolumeControls DefaultVolumeControls { get; private set; } + public VideoCodecBase VideoCodec { get; private set; } + public bool ExcludeFromGlobalFunctions { get; set; } /// @@ -152,11 +155,12 @@ namespace PepperDash.Essentials /// /// public EssentialsHuddleVtc1Room(string key, string name, IRoutingSinkWithSwitching defaultDisplay, - IRoutingSinkNoSwitching defaultAudio, EssentialsHuddleVtc1PropertiesConfig config) + IRoutingSinkNoSwitching defaultAudio, VideoCodecBase vc, EssentialsHuddleVtc1PropertiesConfig config) : base(key, name) { Config = config; DefaultDisplay = defaultDisplay; + VideoCodec = vc; DefaultAudioDevice = defaultAudio; if (defaultAudio is IBasicVolumeControls) DefaultVolumeControls = defaultAudio as IBasicVolumeControls; diff --git a/Essentials/PepperDashEssentials/UI/EssentialsTouchpanelController.cs b/Essentials/PepperDashEssentials/UI/EssentialsTouchpanelController.cs index 6398a2c5..21130a5f 100644 --- a/Essentials/PepperDashEssentials/UI/EssentialsTouchpanelController.cs +++ b/Essentials/PepperDashEssentials/UI/EssentialsTouchpanelController.cs @@ -154,6 +154,9 @@ namespace PepperDash.Essentials { Debug.Console(0, this, "Adding huddle space driver"); var avDriver = new EssentialsHuddleVtc1PanelAvFunctionsDriver(mainDriver, props); + var codecDriver = new PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver(Panel, + (room as EssentialsHuddleVtc1Room).VideoCodec); + avDriver.SetVideoCodecDriver(codecDriver); avDriver.CurrentRoom = room as EssentialsHuddleVtc1Room; avDriver.DefaultRoomKey = props.DefaultRoomKey; mainDriver.AvDriver = avDriver; diff --git a/Essentials/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs b/Essentials/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs index b54e40f6..c055ff77 100644 --- a/Essentials/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs +++ b/Essentials/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs @@ -143,6 +143,10 @@ namespace PepperDash.Essentials /// 1235 /// public const uint VCStagingCameraPress = 1235; + /// + /// 1236 + /// + public const uint VCStagingConnectEnable = 1236; //****************************************************** // Keyboard diff --git a/Essentials/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs b/Essentials/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs index 1d37dca4..70f03b0a 100644 --- a/Essentials/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs +++ b/Essentials/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs @@ -5,6 +5,12 @@ /// public class UIUshortJoin { + // Video Codec + /// + /// 1234 - 0 = Connect, 1 = End + /// + public const uint VCStagingConnectButtonMode = 1234; + /// /// 3812 /// diff --git a/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/HuddleVTCPanelAvFunctionsDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/HuddleVTCPanelAvFunctionsDriver.cs index cb05d1b5..1ce8663d 100644 --- a/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/HuddleVTCPanelAvFunctionsDriver.cs +++ b/Essentials/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/HuddleVTCPanelAvFunctionsDriver.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Collections.Generic; using Crestron.SimplSharp; using Crestron.SimplSharpPro; @@ -127,7 +128,7 @@ namespace PepperDash.Essentials JoinedSigInterlock CallPagesInterlock; - PepperDash.Essentials.UIDrivers.VC.EssentialsCiscoSparkUiDriver VCDriver; + PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver VCDriver; /// /// Constructor @@ -138,8 +139,6 @@ namespace PepperDash.Essentials Config = config; Parent = parent; - VCDriver = new PepperDash.Essentials.UIDrivers.VC.EssentialsCiscoSparkUiDriver(Parent.TriList, null); - PopupInterlock = new JoinedSigInterlock(TriList); StagingBarInterlock = new JoinedSigInterlock(TriList); CallPagesInterlock = new JoinedSigInterlock(TriList); @@ -158,6 +157,15 @@ namespace PepperDash.Essentials //TriList.StringInput[UIStringJoin.StartActivityText].StringValue = "Tap an activity below"; } + /// + /// Add a video codec driver to this + /// + /// + public void SetVideoCodecDriver(PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver vcd) + { + VCDriver = vcd; + } + /// /// /// diff --git a/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsCiscoSparkUiDriver.cs b/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs similarity index 90% rename from Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsCiscoSparkUiDriver.cs rename to Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs index b61f39f6..00a402db 100644 --- a/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsCiscoSparkUiDriver.cs +++ b/Essentials/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs @@ -19,8 +19,11 @@ namespace PepperDash.Essentials.UIDrivers.VC /// Probably needs event or FB to feed AV driver - to show two-mute volume when appropriate. /// /// - public class EssentialsCiscoSparkUiDriver : PanelDriverBase + public class EssentialsVideoCodecUiDriver : PanelDriverBase { + /// + /// + /// VideoCodecBase Codec; /// @@ -62,7 +65,7 @@ namespace PepperDash.Essentials.UIDrivers.VC /// /// /// - public EssentialsCiscoSparkUiDriver(BasicTriListWithSmartObject triList, VideoCodecBase codec) + public EssentialsVideoCodecUiDriver(BasicTriListWithSmartObject triList, VideoCodecBase codec) : base(triList) { Codec = codec; @@ -146,6 +149,7 @@ namespace PepperDash.Essentials.UIDrivers.VC DialKeypad.Misc1.SetSigFalseAction(() => DialKeypadPress("*")); DialKeypad.Misc2SigName = "#"; DialKeypad.Misc2.SetSigFalseAction(() => DialKeypadPress("#")); + TriList.SetSigFalseAction(UIBoolJoin.KeyboardClearPress, DialKeypadBackspacePress); } else Debug.Console(0, "Trilist {0:x2}, VC dial keypad object {1} not found. Check SGD file or VTP", @@ -198,6 +202,13 @@ namespace PepperDash.Essentials.UIDrivers.VC void InCallFeedback_OutputChange(object sender, EventArgs e) { +#warning mode does not change! + var inCall = Codec.InCallFeedback.BoolValue; + Debug.Console(2, "*#* Codec Driver InCallFeedback change={0}", InCall); + TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = (ushort)(inCall ? 1 : 0); + StagingBarInterlock.ShowInterlocked( + inCall ? UIBoolJoin.VCStagingActivePopoverVisible : UIBoolJoin.VCStagingInactivePopoverVisible); + if (Codec.InCallFeedback.BoolValue) // Call is starting { // Header icon @@ -224,12 +235,16 @@ namespace PepperDash.Essentials.UIDrivers.VC DialStringBuilder.Length > 0; } + /// + /// + /// void DialKeypadBackspacePress() { DialStringBuilder.Remove(DialStringBuilder.Length - 1, 1); DialStringFeedback.FireUpdate(); TriList.BooleanInput[UIBoolJoin.KeyboardClearVisible].BoolValue = DialStringBuilder.Length > 0; + TriList.SetBool(UIBoolJoin.VCStagingConnectEnable, DialStringBuilder.Length > 0); } } } \ No newline at end of file diff --git a/Release Package/PepperDashEssentials.cpz b/Release Package/PepperDashEssentials.cpz index 7451c8ca..2232a636 100644 Binary files a/Release Package/PepperDashEssentials.cpz and b/Release Package/PepperDashEssentials.cpz differ diff --git a/Release Package/PepperDashEssentials.dll b/Release Package/PepperDashEssentials.dll index 679cdf05..678cd450 100644 Binary files a/Release Package/PepperDashEssentials.dll and b/Release Package/PepperDashEssentials.dll differ