diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index cd8062ce..42d0e288 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -71,7 +71,7 @@ ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll - + False ..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index 1b936420..ffc6a4db 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -194,7 +194,7 @@ namespace PepperDash.Essentials var vc = VideoCodec as IHasExternalSourceSwitching; if (vc != null) { - vc.SetSelectedSource(_CurrentSourceInfo.SourceKey); + vc.SetSelectedSource(CurrentSourceInfoKey); } } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 2b45030c..e2e6519e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -83,7 +83,7 @@ ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll - + False ..\..\..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll diff --git a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj index 7d6b7bde..69045ea5 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj +++ b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj @@ -59,7 +59,7 @@ ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.UI.dll - + False ..\..\..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraControl.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraControl.cs index 89b6002a..c0a0442c 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraControl.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraControl.cs @@ -92,8 +92,6 @@ namespace PepperDash.Essentials.Devices.Common.Cameras /// public interface IHasCameraPanControl : IHasCameraControls { - // void PanLeft(bool pressRelease); - // void PanRight(bool pressRelease); void PanLeft(); void PanRight(); void PanStop(); @@ -104,8 +102,6 @@ namespace PepperDash.Essentials.Devices.Common.Cameras /// public interface IHasCameraTiltControl : IHasCameraControls { - // void TiltDown(bool pressRelease); - // void TildUp(bool pressRelease); void TiltDown(); void TiltUp(); void TiltStop(); @@ -116,8 +112,6 @@ namespace PepperDash.Essentials.Devices.Common.Cameras /// public interface IHasCameraZoomControl : IHasCameraControls { - // void ZoomIn(bool pressRelease); - // void ZoomOut(bool pressRelease); void ZoomIn(); void ZoomOut(); void ZoomStop(); @@ -135,6 +129,13 @@ namespace PepperDash.Essentials.Devices.Common.Cameras void TriggerAutoFocus(); } + public interface IHasAutoFocusMode + { + void SetFocusModeAuto(); + void SetFocusModeManual(); + void ToggleFocusMode(); + } + public interface IHasCameraAutoMode : IHasCameraControls { void CameraAutoModeOn(); diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs index 2966bde4..fb8ae79c 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Cameras/CameraVisca.cs @@ -12,29 +12,86 @@ using PepperDash.Essentials.Devices.Common.Codec; using System.Text.RegularExpressions; using Crestron.SimplSharp.Reflection; +using Newtonsoft.Json; + namespace PepperDash.Essentials.Devices.Common.Cameras { - public class CameraVisca : CameraBase, IHasCameraPtzControl, ICommunicationMonitor, IHasCameraPresets, IHasPowerControlWithFeedback, IBridgeAdvanced + public class CameraVisca : CameraBase, IHasCameraPtzControl, ICommunicationMonitor, IHasCameraPresets, IHasPowerControlWithFeedback, IBridgeAdvanced, IHasCameraFocusControl, IHasAutoFocusMode { + CameraViscaPropertiesConfig PropertiesConfig; + public IBasicCommunication Communication { get; private set; } - public CommunicationGather PortGather { get; private set; } public StatusMonitorBase CommunicationMonitor { get; private set; } - public byte PanSpeed = 0x10; - public byte TiltSpeed = 0x10; + /// + /// Used to store the actions to parse inquiry responses as the inquiries are sent + /// + private CrestronQueue> InquiryResponseQueue; + + /// + /// Camera ID (Default 1) + /// + public byte ID = 0x01; + public byte ResponseID; + + + public byte PanSpeedSlow = 0x10; + public byte TiltSpeedSlow = 0x10; + + public byte PanSpeedFast = 0x13; + public byte TiltSpeedFast = 0x13; + private bool IsMoving; private bool IsZooming; - public bool PowerIsOn { get; private set; } + + bool _powerIsOn; + public bool PowerIsOn + { + get + { + return _powerIsOn; + } + private set + { + if (value != _powerIsOn) + { + _powerIsOn = value; + PowerIsOnFeedback.FireUpdate(); + CameraIsOffFeedback.FireUpdate(); + } + } + } + + const byte ZoomInCmd = 0x02; + const byte ZoomOutCmd = 0x03; + const byte ZoomStopCmd = 0x00; + + /// + /// Used to determine when to move the camera at a faster speed if a direction is held + /// + CTimer SpeedTimer; + // TODO: Implment speed timer for PTZ controls + + long FastSpeedHoldTimeMs = 2000; byte[] IncomingBuffer = new byte[] { }; public BoolFeedback PowerIsOnFeedback { get; private set; } - public CameraVisca(string key, string name, IBasicCommunication comm, CameraPropertiesConfig props) : + public CameraVisca(string key, string name, IBasicCommunication comm, CameraViscaPropertiesConfig props) : base(key, name) { + InquiryResponseQueue = new CrestronQueue>(15); + Presets = props.Presets; + PropertiesConfig = props; + + ID = (byte)(props.Id + 0x80); + ResponseID = (byte)((props.Id * 0x10) + 0x80); + + SetupCameraSpeeds(); + OutputPorts.Add(new RoutingOutputPort("videoOut", eRoutingSignalType.Video, eRoutingPortConnectionType.None, null, this, true)); // Default to all capabilties @@ -51,11 +108,10 @@ namespace PepperDash.Essentials.Devices.Common.Cameras { // This instance uses RS-232 control } - PortGather = new CommunicationGather(Communication, "\xFF"); - Communication.BytesReceived += new EventHandler(Communication_BytesReceived); PowerIsOnFeedback = new BoolFeedback(() => { return PowerIsOn; }); + CameraIsOffFeedback = new BoolFeedback(() => { return !PowerIsOn; }); if (props.CommunicationMonitorProperties != null) { @@ -66,9 +122,38 @@ namespace PepperDash.Essentials.Devices.Common.Cameras CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 20000, 120000, 300000, "\x81\x09\x04\x00\xFF"); } DeviceManager.AddDevice(CommunicationMonitor); - - } + + + /// + /// Sets up camera speed values based on config + /// + void SetupCameraSpeeds() + { + if (PropertiesConfig.FastSpeedHoldTimeMs > 0) + { + FastSpeedHoldTimeMs = PropertiesConfig.FastSpeedHoldTimeMs; + } + + if (PropertiesConfig.PanSpeedSlow > 0) + { + PanSpeedSlow = (byte)PropertiesConfig.PanSpeedSlow; + } + if (PropertiesConfig.PanSpeedFast > 0) + { + PanSpeedFast = (byte)PropertiesConfig.PanSpeedFast; + } + + if (PropertiesConfig.TiltSpeedSlow > 0) + { + TiltSpeedSlow = (byte)PropertiesConfig.TiltSpeedSlow; + } + if (PropertiesConfig.TiltSpeedFast > 0) + { + TiltSpeedFast = (byte)PropertiesConfig.TiltSpeedFast; + } + } + public override bool CustomActivate() { Communication.Connect(); @@ -110,40 +195,245 @@ namespace PepperDash.Essentials.Devices.Common.Cameras Communication.SendBytes(b); } + void Communication_BytesReceived(object sender, GenericCommMethodReceiveBytesArgs e) { - // This is probably not thread-safe buffering - // Append the incoming bytes with whatever is in the buffer - var newBytes = new byte[IncomingBuffer.Length + e.Bytes.Length]; - IncomingBuffer.CopyTo(newBytes, 0); - e.Bytes.CopyTo(newBytes, IncomingBuffer.Length); - if (Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1 - Debug.Console(2, this, "Received:{0}", ComTextHelper.GetEscapedText(newBytes)); - } + var newBytes = new byte[IncomingBuffer.Length + e.Bytes.Length]; + try + { + // This is probably not thread-safe buffering + // Append the incoming bytes with whatever is in the buffer + IncomingBuffer.CopyTo(newBytes, 0); + e.Bytes.CopyTo(newBytes, IncomingBuffer.Length); + if (Debug.Level == 2) // This check is here to prevent following string format from building unnecessarily on level 0 or 1 + Debug.Console(2, this, "Received:{0}", ComTextHelper.GetEscapedText(newBytes)); - private void SendPanTiltCommand (byte[] cmd) + byte[] message = new byte[] { }; + + // Search for the delimiter 0xFF character + for (int i = 0; i < newBytes.Length; i++) + { + if (newBytes[i] == 0xFF) + { + // i will be the index of the delmiter character + message = newBytes.Take(i).ToArray(); + // Skip over what we just took and save the rest for next time + newBytes = newBytes.Skip(i).ToArray(); + } + } + + if (message.Length > 0) + { + // Check for matching ID + if (message[0] != ResponseID) + { + return; + } + + switch (message[1]) + { + case 0x40: + { + // ACK received + Debug.Console(2, this, "ACK Received"); + break; + } + case 0x50: + { + + if (message[2] == 0xFF) + { + // Completion received + Debug.Console(2, this, "Completion Received"); + } + else + { + // Inquiry response received. Dequeue the next response handler and invoke it + if (InquiryResponseQueue.Count > 0) + { + var inquiryAction = InquiryResponseQueue.Dequeue(); + + inquiryAction.Invoke(message.Skip(2).ToArray()); + } + else + { + Debug.Console(2, this, "Response Queue is empty. Nothing to dequeue."); + } + } + + break; + } + case 0x60: + { + // Error message + + switch (message[2]) + { + case 0x01: + { + // Message Length Error + Debug.Console(2, this, "Error from device: Message Length Error"); + break; + } + case 0x02: + { + // Syntax Error + Debug.Console(2, this, "Error from device: Syntax Error"); + break; + } + case 0x03: + { + // Command Buffer Full + Debug.Console(2, this, "Error from device: Command Buffer Full"); + break; + } + case 0x04: + { + // Command Cancelled + Debug.Console(2, this, "Error from device: Command Cancelled"); + break; + } + case 0x05: + { + // No Socket + Debug.Console(2, this, "Error from device: No Socket"); + break; + } + case 0x41: + { + // Command not executable + Debug.Console(2, this, "Error from device: Command not executable"); + break; + } + } + break; + } + } + + if (message == new byte[] { ResponseID, 0x50, 0x02, 0xFF }) + { + PowerIsOn = true; + } + else if (message == new byte[] { ResponseID, 0x50, 0x03, 0xFF }) + { + PowerIsOn = false; + } + + } + + } + catch (Exception err) + { + Debug.Console(2, this, "Error parsing feedback: {0}", err); + } + finally + { + // Save whatever partial message is here + IncomingBuffer = newBytes; + } + } + + /// + /// Sends a pan/tilt command. If the command is not for fastSpeed then it starts a timer to initiate fast speed. + /// + /// + /// + private void SendPanTiltCommand (byte[] cmd, bool fastSpeedEnabled) { - var temp = new Byte[] { 0x81, 0x01, 0x06, 0x01, PanSpeed, TiltSpeed }; - int length = temp.Length + cmd.Length + 1; - - byte[] sum = new byte[length]; - temp.CopyTo(sum, 0); - cmd.CopyTo(sum, temp.Length); - sum[length - 1] = 0xFF; - SendBytes(sum); + SendBytes(GetPanTiltCommand(cmd, fastSpeedEnabled)); + + if (!fastSpeedEnabled) + { + if (SpeedTimer != null) + { + StopSpeedTimer(); + } + + // Start the timer to send fast speed if still moving after FastSpeedHoldTime elapses + SpeedTimer = new CTimer((o) => SendPanTiltCommand(GetPanTiltCommand(cmd, true), true), FastSpeedHoldTimeMs); + } + } + private void StopSpeedTimer() + { + if (SpeedTimer != null) + { + SpeedTimer.Stop(); + SpeedTimer.Dispose(); + SpeedTimer = null; + } + } + + /// + /// Generates the pan/tilt command with either slow or fast speed + /// + /// + /// + /// + private byte[] GetPanTiltCommand(byte[] cmd, bool fastSpeed) + { + byte panSpeed; + byte tiltSpeed; + + if (!fastSpeed) + { + panSpeed = PanSpeedSlow; + tiltSpeed = TiltSpeedSlow; + } + else + { + panSpeed = PanSpeedFast; + tiltSpeed = TiltSpeedFast; + } + + var temp = new byte[] { ID, 0x01, 0x06, 0x01, panSpeed, tiltSpeed }; + int length = temp.Length + cmd.Length + 1; + + byte[] sum = new byte[length]; + temp.CopyTo(sum, 0); + cmd.CopyTo(sum, temp.Length); + sum[length - 1] = 0xFF; + + return sum; + } + + + void SendPowerQuery() + { + SendBytes(new byte[] { ID, 0x09, 0x04, 0x00, 0xFF }); + InquiryResponseQueue.Enqueue(HandlePowerResponse); + } + public void PowerOn() { - - SendBytes(new Byte[] { 0x81, 0x01, 0x04, 0x00, 0x02, 0xFF }); + SendBytes(new byte[] { ID, 0x01, 0x04, 0x00, 0x02, 0xFF }); + SendPowerQuery(); } + void HandlePowerResponse(byte[] response) + { + switch (response[0]) + { + case 0x02: + { + PowerIsOn = true; + break; + } + case 0x03: + { + PowerIsOn = false; + break; + } + } + } + public void PowerOff() { - SendBytes(new Byte[] {0x81, 0x01, 0x04, 0x00, 0x03, 0xFF}); - } + SendBytes(new byte[] {ID, 0x01, 0x04, 0x00, 0x03, 0xFF}); + SendPowerQuery(); + } public void PowerToggle() { @@ -155,12 +445,12 @@ namespace PepperDash.Essentials.Devices.Common.Cameras public void PanLeft() { - SendPanTiltCommand(new byte[] {0x01, 0x03}); + SendPanTiltCommand(new byte[] {0x01, 0x03}, false); IsMoving = true; } public void PanRight() { - SendPanTiltCommand(new byte[] { 0x02, 0x03 }); + SendPanTiltCommand(new byte[] { 0x02, 0x03 }, false); IsMoving = true; } public void PanStop() @@ -169,12 +459,12 @@ namespace PepperDash.Essentials.Devices.Common.Cameras } public void TiltDown() { - SendPanTiltCommand(new byte[] { 0x03, 0x02 }); + SendPanTiltCommand(new byte[] { 0x03, 0x02 }, false); IsMoving = true; } public void TiltUp() { - SendPanTiltCommand(new byte[] { 0x03, 0x01 }); + SendPanTiltCommand(new byte[] { 0x03, 0x01 }, false); IsMoving = true; } public void TiltStop() @@ -184,16 +474,18 @@ namespace PepperDash.Essentials.Devices.Common.Cameras private void SendZoomCommand (byte cmd) { - SendBytes(new byte[] {0x81, 0x01, 0x04, 0x07, cmd, 0xFF} ); + SendBytes(new byte[] {ID, 0x01, 0x04, 0x07, cmd, 0xFF} ); } + + public void ZoomIn() { - SendZoomCommand(0x02); + SendZoomCommand(ZoomInCmd); IsZooming = true; } public void ZoomOut() { - SendZoomCommand(0x03); + SendZoomCommand(ZoomOutCmd); IsZooming = true; } public void ZoomStop() @@ -205,26 +497,28 @@ namespace PepperDash.Essentials.Devices.Common.Cameras { if (IsZooming) { - SendZoomCommand(0x00); + SendZoomCommand(ZoomStopCmd); IsZooming = false; } else { - SendPanTiltCommand(new byte[] {0x03, 0x03}); + StopSpeedTimer(); + SendPanTiltCommand(new byte[] { 0x03, 0x03 }, false); IsMoving = false; } } public void PositionHome() { - throw new NotImplementedException(); + SendBytes(new byte[] { ID, 0x01, 0x06, 0x02, PanSpeedFast, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }); + SendBytes(new byte[] { ID, 0x01, 0x04, 0x47, 0x00, 0x00, 0x00, 0x00, 0xFF }); } public void RecallPreset(int presetNumber) { - SendBytes(new byte[] {0x81, 0x01, 0x04, 0x3F, 0x02, (byte)presetNumber, 0xFF} ); + SendBytes(new byte[] {ID, 0x01, 0x04, 0x3F, 0x02, (byte)presetNumber, 0xFF} ); } public void SavePreset(int presetNumber) { - SendBytes(new byte[] { 0x81, 0x01, 0x04, 0x3F, 0x01, (byte)presetNumber, 0xFF }); + SendBytes(new byte[] { ID, 0x01, 0x04, 0x3F, 0x01, (byte)presetNumber, 0xFF }); } #region IHasCameraPresets Members @@ -244,6 +538,90 @@ namespace PepperDash.Essentials.Devices.Common.Cameras } #endregion + + #region IHasCameraFocusControl Members + + public void FocusNear() + { + SendBytes(new byte[] { ID, 0x01, 0x04, 0x08, 0x03, 0xFF }); + } + + public void FocusFar() + { + SendBytes(new byte[] { ID, 0x01, 0x04, 0x08, 0x02, 0xFF }); + } + + public void FocusStop() + { + SendBytes(new byte[] { ID, 0x01, 0x04, 0x08, 0x00, 0xFF }); + } + + public void TriggerAutoFocus() + { + SendBytes(new byte[] { ID, 0x01, 0x04, 0x18, 0x01, 0xFF }); + SendAutoFocusQuery(); + } + + #endregion + + #region IHasAutoFocus Members + + public void SetFocusModeAuto() + { + SendBytes(new byte[] { ID, 0x01, 0x04, 0x38, 0x02, 0xFF }); + SendAutoFocusQuery(); + } + + public void SetFocusModeManual() + { + SendBytes(new byte[] { ID, 0x01, 0x04, 0x38, 0x03, 0xFF }); + SendAutoFocusQuery(); + } + + public void ToggleFocusMode() + { + SendBytes(new byte[] { ID, 0x01, 0x04, 0x38, 0x10, 0xFF }); + SendAutoFocusQuery(); + } + + #endregion + + void SendAutoFocusQuery() + { + SendBytes(new byte[] { ID, 0x09, 0x04, 0x38, 0xFF }); + InquiryResponseQueue.Enqueue(HandleAutoFocusResponse); + } + + void HandleAutoFocusResponse(byte[] response) + { + switch (response[0]) + { + case 0x02: + { + // Auto Mode + PowerIsOn = true; + break; + } + case 0x03: + { + // Manual Mode + PowerIsOn = false; + break; + } + } + } + + #region IHasCameraOff Members + + public BoolFeedback CameraIsOffFeedback { get; private set; } + + + public void CameraOff() + { + PowerOff(); + } + + #endregion } public class CameraViscaFactory : EssentialsDeviceFactory @@ -257,10 +635,51 @@ namespace PepperDash.Essentials.Devices.Common.Cameras { Debug.Console(1, "Factory Attempting to create new CameraVisca Device"); var comm = CommFactory.CreateCommForDevice(dc); - var props = Newtonsoft.Json.JsonConvert.DeserializeObject( + var props = Newtonsoft.Json.JsonConvert.DeserializeObject( dc.Properties.ToString()); return new Cameras.CameraVisca(dc.Key, dc.Name, comm, props); } } + + public class CameraViscaPropertiesConfig : CameraPropertiesConfig + { + /// + /// Control ID of the camera (1-7) + /// + [JsonProperty("id")] + public uint Id { get; set; } + + /// + /// Slow Pan speed (0-18) + /// + [JsonProperty("panSpeedSlow")] + public uint PanSpeedSlow { get; set; } + + /// + /// Fast Pan speed (0-18) + /// + [JsonProperty("panSpeedFast")] + public uint PanSpeedFast { get; set; } + + /// + /// Slow tilt speed (0-18) + /// + [JsonProperty("tiltSpeedSlow")] + public uint TiltSpeedSlow { get; set; } + + /// + /// Fast tilt speed (0-18) + /// + [JsonProperty("tiltSpeedFast")] + public uint TiltSpeedFast { get; set; } + + /// + /// Time a button must be held before fast speed is engaged (Milliseconds) + /// + [JsonProperty("fastSpeedHoldTimeMs")] + public uint FastSpeedHoldTimeMs { get; set; } + + } + } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj index f115f401..54d37613 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj @@ -63,7 +63,7 @@ ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SSPDevices\Crestron.SimplSharpPro.Lighting.dll - + False ..\..\..\packages\PepperDashCore\lib\net35\PepperDash_Core.dll diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs index ce748699..429794bc 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs @@ -1339,10 +1339,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public override void SendDtmf(string s) { - if (CallFavorites != null) - { - SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s)); - } + SendText(string.Format("xCommand Call DTMFSend CallId: {0} DTMFString: \"{1}\"", GetCallId(), s)); } public void SelectPresentationSource(int source)