mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-12 11:15:08 +00:00
Removes essentials-framework as a submodule and brings the files back into the main repo
This commit is contained in:
@@ -0,0 +1,376 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
|
||||
// QUESTIONS:
|
||||
//
|
||||
// When subscribing, just use the Instance ID for Custom Name?
|
||||
|
||||
// Verbose on subscriptions?
|
||||
|
||||
// Example subscription feedback responses
|
||||
// ! "publishToken":"name" "value":-77.0
|
||||
// ! "myLevelName" -77
|
||||
|
||||
public class BiampTesiraForteDsp : DspBase
|
||||
{
|
||||
public IBasicCommunication Communication { get; private set; }
|
||||
public CommunicationGather PortGather { get; private set; }
|
||||
public StatusMonitorBase CommunicationMonitor { get; private set; }
|
||||
|
||||
new public Dictionary<string, TesiraForteLevelControl> LevelControlPoints { get; private set; }
|
||||
|
||||
public bool isSubscribed;
|
||||
|
||||
private CTimer SubscriptionTimer;
|
||||
|
||||
CrestronQueue CommandQueue;
|
||||
|
||||
bool CommandQueueInProgress = false;
|
||||
|
||||
//new public Dictionary<string, DspControlPoint> DialerControlPoints { get; private set; }
|
||||
|
||||
//new public Dictionary<string, DspControlPoint> SwitcherControlPoints { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Shows received lines as hex
|
||||
/// </summary>
|
||||
public bool ShowHexResponse { get; set; }
|
||||
|
||||
public BiampTesiraForteDsp(string key, string name, IBasicCommunication comm, BiampTesiraFortePropertiesConfig props) :
|
||||
base(key, name)
|
||||
{
|
||||
CommandQueue = new CrestronQueue(100);
|
||||
|
||||
Communication = comm;
|
||||
var socket = comm as ISocketStatus;
|
||||
if (socket != null)
|
||||
{
|
||||
// This instance uses IP control
|
||||
|
||||
socket.ConnectionChange += new EventHandler<GenericSocketStatusChageEventArgs>(socket_ConnectionChange);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This instance uses RS-232 control
|
||||
}
|
||||
PortGather = new CommunicationGather(Communication, "\x0d\x0a");
|
||||
PortGather.LineReceived += this.Port_LineReceived;
|
||||
if (props.CommunicationMonitorProperties != null)
|
||||
{
|
||||
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
//#warning Need to deal with this poll string
|
||||
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 120000, 120000, 300000, "SESSION get aliases\x0d\x0a");
|
||||
}
|
||||
|
||||
LevelControlPoints = new Dictionary<string, TesiraForteLevelControl>();
|
||||
|
||||
foreach (KeyValuePair<string, BiampTesiraForteLevelControlBlockConfig> block in props.LevelControlBlocks)
|
||||
{
|
||||
this.LevelControlPoints.Add(block.Key, new TesiraForteLevelControl(block.Key, block.Value, this));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override bool CustomActivate()
|
||||
{
|
||||
Communication.Connect();
|
||||
CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); };
|
||||
CommunicationMonitor.Start();
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
return true;
|
||||
}
|
||||
|
||||
void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)
|
||||
{
|
||||
Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString());
|
||||
|
||||
if (e.Client.IsConnected)
|
||||
{
|
||||
// Tasks on connect
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cleanup items from this session
|
||||
|
||||
if (SubscriptionTimer != null)
|
||||
{
|
||||
SubscriptionTimer.Stop();
|
||||
SubscriptionTimer = null;
|
||||
}
|
||||
|
||||
isSubscribed = false;
|
||||
CommandQueue.Clear();
|
||||
CommandQueueInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the subscription process to the DSP
|
||||
/// </summary>
|
||||
void SubscribeToAttributes()
|
||||
{
|
||||
SendLine("SESSION set verbose true");
|
||||
|
||||
foreach (KeyValuePair<string, TesiraForteLevelControl> level in LevelControlPoints)
|
||||
{
|
||||
level.Value.Subscribe();
|
||||
}
|
||||
|
||||
if (!CommandQueueInProgress)
|
||||
SendNextQueuedCommand();
|
||||
|
||||
ResetSubscriptionTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets or Sets the subscription timer
|
||||
/// </summary>
|
||||
void ResetSubscriptionTimer()
|
||||
{
|
||||
isSubscribed = true;
|
||||
|
||||
if (SubscriptionTimer != null)
|
||||
{
|
||||
SubscriptionTimer = new CTimer(o => SubscribeToAttributes(), 30000);
|
||||
SubscriptionTimer.Reset();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles a response message from the DSP
|
||||
/// </summary>
|
||||
/// <param name="dev"></param>
|
||||
/// <param name="args"></param>
|
||||
void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args)
|
||||
{
|
||||
if (Debug.Level == 2)
|
||||
Debug.Console(2, this, "RX: '{0}'",
|
||||
ShowHexResponse ? ComTextHelper.GetEscapedText(args.Text) : args.Text);
|
||||
|
||||
Debug.Console(1, this, "RX: '{0}'", args.Text);
|
||||
|
||||
try
|
||||
{
|
||||
if (args.Text.IndexOf("Welcome to the Tesira Text Protocol Server...") > -1)
|
||||
{
|
||||
// Indicates a new TTP session
|
||||
|
||||
SubscribeToAttributes();
|
||||
}
|
||||
else if (args.Text.IndexOf("publishToken") > -1)
|
||||
{
|
||||
// response is from a subscribed attribute
|
||||
|
||||
string pattern = "! \"publishToken\":[\"](.*)[\"] \"value\":(.*)";
|
||||
|
||||
Match match = Regex.Match(args.Text, pattern);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
|
||||
string key;
|
||||
|
||||
string customName;
|
||||
|
||||
string value;
|
||||
|
||||
customName = match.Groups[1].Value;
|
||||
|
||||
// Finds the key (everything before the '~' character
|
||||
key = customName.Substring(0, customName.IndexOf("~", 0) - 1);
|
||||
|
||||
value = match.Groups[2].Value;
|
||||
|
||||
foreach (KeyValuePair<string, TesiraForteLevelControl> controlPoint in LevelControlPoints)
|
||||
{
|
||||
if (customName == controlPoint.Value.LevelCustomName || customName == controlPoint.Value.MuteCustomName)
|
||||
{
|
||||
controlPoint.Value.ParseSubscriptionMessage(customName, value);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// same for dialers
|
||||
/// same for switchers
|
||||
|
||||
}
|
||||
else if (args.Text.IndexOf("+OK") > -1)
|
||||
{
|
||||
if (args.Text == "+OK" || args.Text.IndexOf("list\":") > -1 ) // Check for a simple "+OK" only 'ack' repsonse or a list response and ignore
|
||||
return;
|
||||
|
||||
// response is not from a subscribed attribute. From a get/set/toggle/increment/decrement command
|
||||
|
||||
if (!CommandQueue.IsEmpty)
|
||||
{
|
||||
if (CommandQueue.Peek() is QueuedCommand)
|
||||
{
|
||||
// Expected response belongs to a child class
|
||||
QueuedCommand tempCommand = (QueuedCommand)CommandQueue.TryToDequeue();
|
||||
//Debug.Console(1, this, "Command Dequeued. CommandQueue Size: {0}", CommandQueue.Count);
|
||||
|
||||
tempCommand.ControlPoint.ParseGetMessage(tempCommand.AttributeCode, args.Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Expected response belongs to this class
|
||||
string temp = (string)CommandQueue.TryToDequeue();
|
||||
//Debug.Console(1, this, "Command Dequeued. CommandQueue Size: {0}", CommandQueue.Count);
|
||||
|
||||
}
|
||||
|
||||
if (CommandQueue.IsEmpty)
|
||||
CommandQueueInProgress = false;
|
||||
else
|
||||
SendNextQueuedCommand();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else if (args.Text.IndexOf("-ERR") > -1)
|
||||
{
|
||||
// Error response
|
||||
|
||||
switch (args.Text)
|
||||
{
|
||||
case "-ERR ALREADY_SUBSCRIBED":
|
||||
{
|
||||
ResetSubscriptionTimer();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Debug.Console(0, this, "Error From DSP: '{0}'", args.Text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Debug.Level == 2)
|
||||
Debug.Console(2, this, "Error parsing response: '{0}'\n{1}", args.Text, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a command to the DSP (with delimiter appended)
|
||||
/// </summary>
|
||||
/// <param name="s">Command to send</param>
|
||||
public void SendLine(string s)
|
||||
{
|
||||
Debug.Console(1, this, "TX: '{0}'", s);
|
||||
Communication.SendText(s + "\x0a");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command from a child module to the queue
|
||||
/// </summary>
|
||||
/// <param name="command">Command object from child module</param>
|
||||
public void EnqueueCommand(QueuedCommand commandToEnqueue)
|
||||
{
|
||||
CommandQueue.Enqueue(commandToEnqueue);
|
||||
//Debug.Console(1, this, "Command (QueuedCommand) Enqueued '{0}'. CommandQueue has '{1}' Elements.", commandToEnqueue.Command, CommandQueue.Count);
|
||||
|
||||
if(!CommandQueueInProgress)
|
||||
SendNextQueuedCommand();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a raw string command to the queue
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
public void EnqueueCommand(string command)
|
||||
{
|
||||
CommandQueue.Enqueue(command);
|
||||
//Debug.Console(1, this, "Command (string) Enqueued '{0}'. CommandQueue has '{1}' Elements.", command, CommandQueue.Count);
|
||||
|
||||
if (!CommandQueueInProgress)
|
||||
SendNextQueuedCommand();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the next queued command to the DSP
|
||||
/// </summary>
|
||||
void SendNextQueuedCommand()
|
||||
{
|
||||
//Debug.Console(2, this, "Attempting to send next queued command. CommandQueueInProgress: {0} Communication isConnected: {1}", CommandQueueInProgress, Communication.IsConnected);
|
||||
|
||||
//if (CommandQueue.IsEmpty)
|
||||
// CommandQueueInProgress = false;
|
||||
|
||||
//Debug.Console(1, this, "CommandQueue has {0} Elements:\n", CommandQueue.Count);
|
||||
|
||||
//foreach (object o in CommandQueue)
|
||||
//{
|
||||
// if (o is string)
|
||||
// Debug.Console(1, this, "{0}", o);
|
||||
// else if(o is QueuedCommand)
|
||||
// {
|
||||
// var item = (QueuedCommand)o;
|
||||
// Debug.Console(1, this, "{0}", item.Command);
|
||||
// }
|
||||
//}
|
||||
|
||||
//Debug.Console(1, this, "End of CommandQueue");
|
||||
|
||||
if (Communication.IsConnected && !CommandQueue.IsEmpty)
|
||||
{
|
||||
CommandQueueInProgress = true;
|
||||
|
||||
if (CommandQueue.Peek() is QueuedCommand)
|
||||
{
|
||||
QueuedCommand nextCommand = new QueuedCommand();
|
||||
|
||||
nextCommand = (QueuedCommand)CommandQueue.Peek();
|
||||
|
||||
SendLine(nextCommand.Command);
|
||||
}
|
||||
else
|
||||
{
|
||||
string nextCommand = (string)CommandQueue.Peek();
|
||||
|
||||
SendLine(nextCommand);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a command to execute a preset
|
||||
/// </summary>
|
||||
/// <param name="name">Preset Name</param>
|
||||
public override void RunPreset(string name)
|
||||
{
|
||||
SendLine(string.Format("DEVICE recallPreset {0}", name));
|
||||
}
|
||||
|
||||
public class QueuedCommand
|
||||
{
|
||||
public string Command { get; set; }
|
||||
public string AttributeCode { get; set; }
|
||||
public TesiraForteControlPoint ControlPoint { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
public class TesiraForteLevelControl : TesiraForteControlPoint, IBiampTesiraDspLevelControl, IKeyed
|
||||
{
|
||||
bool _IsMuted;
|
||||
ushort _VolumeLevel;
|
||||
|
||||
public BoolFeedback MuteFeedback { get; private set; }
|
||||
|
||||
public IntFeedback VolumeLevelFeedback { get; private set; }
|
||||
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public string ControlPointTag { get { return base.InstanceTag; } }
|
||||
|
||||
/// <summary>
|
||||
/// Used for to identify level subscription values
|
||||
/// </summary>
|
||||
public string LevelCustomName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used for to identify mute subscription values
|
||||
/// </summary>
|
||||
public string MuteCustomName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum fader level
|
||||
/// </summary>
|
||||
double MinLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum fader level
|
||||
/// </summary>
|
||||
double MaxLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a valid subscription string has been recieved for all subscriptions
|
||||
/// </summary>
|
||||
public bool IsSubsribed
|
||||
{
|
||||
get
|
||||
{
|
||||
bool isSubscribed = false;
|
||||
|
||||
if (HasMute && MuteIsSubscribed)
|
||||
isSubscribed = true;
|
||||
|
||||
if (HasLevel && LevelIsSubscribed)
|
||||
isSubscribed = true;
|
||||
|
||||
return isSubscribed;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AutomaticUnmuteOnVolumeUp { get; private set; }
|
||||
|
||||
public bool HasMute { get; private set; }
|
||||
|
||||
public bool HasLevel { get; private set; }
|
||||
|
||||
bool MuteIsSubscribed;
|
||||
|
||||
bool LevelIsSubscribed;
|
||||
|
||||
//public TesiraForteLevelControl(string label, string id, int index1, int index2, bool hasMute, bool hasLevel, BiampTesiraForteDsp parent)
|
||||
// : base(id, index1, index2, parent)
|
||||
//{
|
||||
// Initialize(label, hasMute, hasLevel);
|
||||
//}
|
||||
|
||||
public TesiraForteLevelControl(string key, BiampTesiraForteLevelControlBlockConfig config, BiampTesiraForteDsp parent)
|
||||
: base(config.InstanceTag, config.Index1, config.Index2, parent)
|
||||
{
|
||||
Initialize(key, config.Label, config.HasMute, config.HasLevel);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this attribute based on config values and generates subscriptions commands and adds commands to the parent's queue.
|
||||
/// </summary>
|
||||
public void Initialize(string key, string label, bool hasMute, bool hasLevel)
|
||||
{
|
||||
Key = string.Format("{0}--{1}", Parent.Key, key);
|
||||
|
||||
DeviceManager.AddDevice(this);
|
||||
|
||||
Debug.Console(2, this, "Adding LevelControl '{0}'", Key);
|
||||
|
||||
this.IsSubscribed = false;
|
||||
|
||||
MuteFeedback = new BoolFeedback(() => _IsMuted);
|
||||
|
||||
VolumeLevelFeedback = new IntFeedback(() => _VolumeLevel);
|
||||
|
||||
HasMute = hasMute;
|
||||
HasLevel = hasLevel;
|
||||
}
|
||||
|
||||
public void Subscribe()
|
||||
{
|
||||
// Do subscriptions and blah blah
|
||||
|
||||
// Subscribe to mute
|
||||
if (this.HasMute)
|
||||
{
|
||||
MuteCustomName = string.Format("{0}~mute{1}", this.InstanceTag, this.Index1);
|
||||
|
||||
SendSubscriptionCommand(MuteCustomName, "mute", 500);
|
||||
}
|
||||
|
||||
// Subscribe to level
|
||||
if (this.HasLevel)
|
||||
{
|
||||
LevelCustomName = string.Format("{0}~level{1}", this.InstanceTag, this.Index1);
|
||||
|
||||
SendSubscriptionCommand(LevelCustomName, "level", 250);
|
||||
|
||||
SendFullCommand("get", "minLevel", null);
|
||||
|
||||
SendFullCommand("get", "maxLevel", null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Parses the response from the DspBase
|
||||
/// </summary>
|
||||
/// <param name="customName"></param>
|
||||
/// <param name="value"></param>
|
||||
public void ParseSubscriptionMessage(string customName, string value)
|
||||
{
|
||||
|
||||
// Check for valid subscription response
|
||||
|
||||
if (this.HasMute && customName == MuteCustomName)
|
||||
{
|
||||
//if (value.IndexOf("+OK") > -1)
|
||||
//{
|
||||
// int pointer = value.IndexOf(" +OK");
|
||||
|
||||
// MuteIsSubscribed = true;
|
||||
|
||||
// // Removes the +OK
|
||||
// value = value.Substring(0, value.Length - (value.Length - (pointer - 1)));
|
||||
//}
|
||||
|
||||
if (value.IndexOf("true") > -1)
|
||||
{
|
||||
_IsMuted = true;
|
||||
MuteIsSubscribed = true;
|
||||
|
||||
}
|
||||
else if (value.IndexOf("false") > -1)
|
||||
{
|
||||
_IsMuted = false;
|
||||
MuteIsSubscribed = true;
|
||||
}
|
||||
|
||||
MuteFeedback.FireUpdate();
|
||||
}
|
||||
else if (this.HasLevel && customName == LevelCustomName)
|
||||
{
|
||||
//if (value.IndexOf("+OK") > -1)
|
||||
//{
|
||||
// int pointer = value.IndexOf(" +OK");
|
||||
|
||||
// LevelIsSubscribed = true;
|
||||
|
||||
//}
|
||||
|
||||
var _value = Double.Parse(value);
|
||||
|
||||
_VolumeLevel = (ushort)Scale(_value, MinLevel, MaxLevel, 0, 65535);
|
||||
|
||||
LevelIsSubscribed = true;
|
||||
|
||||
VolumeLevelFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a non subscription response
|
||||
/// </summary>
|
||||
/// <param name="attributeCode">The attribute code of the command</param>
|
||||
/// <param name="message">The message to parse</param>
|
||||
public override void ParseGetMessage(string attributeCode, string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parse an "+OK" message
|
||||
string pattern = @"\+OK ""value"":(.*)";
|
||||
|
||||
Match match = Regex.Match(message, pattern);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
|
||||
string value = match.Groups[1].Value;
|
||||
|
||||
Debug.Console(1, this, "Response: '{0}' Value: '{1}'", attributeCode, value);
|
||||
|
||||
if (message.IndexOf("\"value\":") > -1)
|
||||
{
|
||||
switch (attributeCode)
|
||||
{
|
||||
case "minLevel":
|
||||
{
|
||||
MinLevel = Double.Parse(value);
|
||||
|
||||
Debug.Console(1, this, "MinLevel is '{0}'", MinLevel);
|
||||
|
||||
break;
|
||||
}
|
||||
case "maxLevel":
|
||||
{
|
||||
MaxLevel = Double.Parse(value);
|
||||
|
||||
Debug.Console(1, this, "MaxLevel is '{0}'", MaxLevel);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Debug.Console(2, "Response does not match expected attribute codes: '{0}'", message);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Console(2, "Unable to parse message: '{0}'\n{1}", message, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns the mute off
|
||||
/// </summary>
|
||||
public void MuteOff()
|
||||
{
|
||||
SendFullCommand("set", "mute", "false");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns the mute on
|
||||
/// </summary>
|
||||
public void MuteOn()
|
||||
{
|
||||
SendFullCommand("set", "mute", "true");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the volume to a specified level
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
public void SetVolume(ushort level)
|
||||
{
|
||||
Debug.Console(1, this, "volume: {0}", level);
|
||||
// Unmute volume if new level is higher than existing
|
||||
if (level > _VolumeLevel && AutomaticUnmuteOnVolumeUp)
|
||||
if(_IsMuted)
|
||||
MuteOff();
|
||||
|
||||
double volumeLevel = Scale(level, 0, 65535, MinLevel, MaxLevel);
|
||||
|
||||
SendFullCommand("set", "level", string.Format("{0:0.000000}", volumeLevel));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles mute status
|
||||
/// </summary>
|
||||
public void MuteToggle()
|
||||
{
|
||||
SendFullCommand("toggle", "mute", "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrements volume level
|
||||
/// </summary>
|
||||
/// <param name="pressRelease"></param>
|
||||
public void VolumeDown(bool pressRelease)
|
||||
{
|
||||
SendFullCommand("decrement", "level", "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments volume level
|
||||
/// </summary>
|
||||
/// <param name="pressRelease"></param>
|
||||
public void VolumeUp(bool pressRelease)
|
||||
{
|
||||
SendFullCommand("increment", "level", "");
|
||||
|
||||
if (AutomaticUnmuteOnVolumeUp)
|
||||
if (!_IsMuted)
|
||||
MuteOff();
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Scales the input from the input range to the output range
|
||||
///// </summary>
|
||||
///// <param name="input"></param>
|
||||
///// <param name="inMin"></param>
|
||||
///// <param name="inMax"></param>
|
||||
///// <param name="outMin"></param>
|
||||
///// <param name="outMax"></param>
|
||||
///// <returns></returns>
|
||||
//int Scale(int input, int inMin, int inMax, int outMin, int outMax)
|
||||
//{
|
||||
// Debug.Console(1, this, "Scaling (int) input '{0}' with min '{1}'/max '{2}' to output range min '{3}'/max '{4}'", input, inMin, inMax, outMin, outMax);
|
||||
|
||||
// int inputRange = inMax - inMin;
|
||||
|
||||
// int outputRange = outMax - outMin;
|
||||
|
||||
// var output = (((input-inMin) * outputRange) / inputRange ) - outMin;
|
||||
|
||||
// Debug.Console(1, this, "Scaled output '{0}'", output);
|
||||
|
||||
// return output;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the input from the input range to the output range
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="inMin"></param>
|
||||
/// <param name="inMax"></param>
|
||||
/// <param name="outMin"></param>
|
||||
/// <param name="outMax"></param>
|
||||
/// <returns></returns>
|
||||
double Scale(double input, double inMin, double inMax, double outMin, double outMax)
|
||||
{
|
||||
Debug.Console(1, this, "Scaling (double) input '{0}' with min '{1}'/max '{2}' to output range min '{3}'/max '{4}'",input ,inMin ,inMax ,outMin, outMax);
|
||||
|
||||
double inputRange = inMax - inMin;
|
||||
|
||||
if (inputRange <= 0)
|
||||
{
|
||||
throw new ArithmeticException(string.Format("Invalid Input Range '{0}' for Scaling. Min '{1}' Max '{2}'.", inputRange, inMin, inMax));
|
||||
}
|
||||
|
||||
double outputRange = outMax - outMin;
|
||||
|
||||
var output = (((input - inMin) * outputRange) / inputRange) + outMin;
|
||||
|
||||
Debug.Console(1, this, "Scaled output '{0}'", output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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.Devices.Common.DSP
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class BiampTesiraFortePropertiesConfig
|
||||
{
|
||||
public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; }
|
||||
|
||||
public ControlPropertiesConfig Control { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// These are key-value pairs, string id, string type.
|
||||
/// Valid types are level and mute.
|
||||
/// Need to include the index values somehow
|
||||
/// </summary>
|
||||
public Dictionary<string, BiampTesiraForteLevelControlBlockConfig> LevelControlBlocks { get; set; }
|
||||
// public Dictionary<string, BiampTesiraForteDialerControlBlockConfig> DialerControlBlocks {get; set;}
|
||||
}
|
||||
|
||||
public class BiampTesiraForteLevelControlBlockConfig
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public string Label { get; set; }
|
||||
public string InstanceTag { get; set; }
|
||||
public int Index1 { get; set; }
|
||||
public int Index2 { get; set; }
|
||||
public bool HasMute { get; set; }
|
||||
public bool HasLevel { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
public abstract class TesiraForteControlPoint : DspControlPoint
|
||||
{
|
||||
public string Key { get; protected set; }
|
||||
|
||||
public string InstanceTag { get; set; }
|
||||
public int Index1 { get; private set; }
|
||||
public int Index2 { get; private set; }
|
||||
public BiampTesiraForteDsp Parent { get; private set; }
|
||||
|
||||
public bool IsSubscribed { get; protected set; }
|
||||
|
||||
protected TesiraForteControlPoint(string id, int index1, int index2, BiampTesiraForteDsp parent)
|
||||
{
|
||||
InstanceTag = id;
|
||||
Index1 = index1;
|
||||
Index2 = index2;
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
virtual public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a command to the DSP
|
||||
/// </summary>
|
||||
/// <param name="command">command</param>
|
||||
/// <param name="attribute">attribute code</param>
|
||||
/// <param name="value">value (use "" if not applicable)</param>
|
||||
public virtual void SendFullCommand(string command, string attributeCode, string value)
|
||||
{
|
||||
// Command Format: InstanceTag get/set/toggle/increment/decrement/subscribe/unsubscribe attributeCode [index] [value]
|
||||
// Ex: "RoomLevel set level 1.00"
|
||||
|
||||
string cmd;
|
||||
|
||||
if (attributeCode == "level" || attributeCode == "mute" || attributeCode == "minLevel" || attributeCode == "maxLevel" || attributeCode == "label" || attributeCode == "rampInterval" || attributeCode == "rampStep")
|
||||
{
|
||||
//Command requires Index
|
||||
|
||||
if (String.IsNullOrEmpty(value))
|
||||
{
|
||||
// format command without value
|
||||
cmd = string.Format("{0} {1} {2} {3}", InstanceTag, command, attributeCode, Index1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// format commadn with value
|
||||
cmd = string.Format("{0} {1} {2} {3} {4}", InstanceTag, command, attributeCode, Index1, value);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//Command does not require Index
|
||||
|
||||
if (String.IsNullOrEmpty(value))
|
||||
{
|
||||
cmd = string.Format("{0} {1} {2} {3}", InstanceTag, command, attributeCode, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = string.Format("{0} {1} {2}", InstanceTag, command, attributeCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (command == "get")
|
||||
{
|
||||
// This command will generate a return value response so it needs to be queued
|
||||
Parent.EnqueueCommand(new BiampTesiraForteDsp.QueuedCommand{ Command = cmd, AttributeCode = attributeCode, ControlPoint = this });
|
||||
}
|
||||
else
|
||||
{
|
||||
// This command will generate a simple "+OK" response and doesn't need to be queued
|
||||
Parent.SendLine(cmd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual public void ParseGetMessage(string attributeCode, string message)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
public virtual void SendSubscriptionCommand(string customName, string attributeCode, int responseRate)
|
||||
{
|
||||
// Subscription string format: InstanceTag subscribe attributeCode Index1 customName responseRate
|
||||
// Ex: "RoomLevel subscribe level 1 MyRoomLevel 500"
|
||||
|
||||
string cmd;
|
||||
|
||||
if (responseRate > 0)
|
||||
{
|
||||
cmd = string.Format("{0} subscribe {1} {2} {3} {4}", InstanceTag, attributeCode, Index1, customName, responseRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = string.Format("{0} subscribe {1} {2} {3}", InstanceTag, attributeCode, Index1, customName);
|
||||
}
|
||||
|
||||
Parent.SendLine(cmd);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
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.Devices.Common.DSP
|
||||
{
|
||||
|
||||
//QUESTIONS:
|
||||
|
||||
//When subscribing, just use the Instance ID for Custom Name?
|
||||
|
||||
//Verbose on subscriptions?
|
||||
|
||||
//! "publishToken":"name" "value":-77.0
|
||||
//! "myLevelName" -77
|
||||
|
||||
//public class TesiraForteMuteControl : IDspLevelControl
|
||||
//{
|
||||
// BiampTesiraForteDsp Parent;
|
||||
// bool _IsMuted;
|
||||
// ushort _VolumeLevel;
|
||||
|
||||
// public TesiraForteMuteControl(string id, BiampTesiraForteDsp parent)
|
||||
// : base(id)
|
||||
// {
|
||||
// Parent = parent;
|
||||
// }
|
||||
|
||||
// public void Initialize()
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
// protected override Func<bool> MuteFeedbackFunc
|
||||
// {
|
||||
// get { return () => _IsMuted; }
|
||||
// }
|
||||
|
||||
// protected override Func<int> VolumeLevelFeedbackFunc
|
||||
// {
|
||||
// get { return () => _VolumeLevel; }
|
||||
// }
|
||||
|
||||
// public override void MuteOff()
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public override void MuteOn()
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public override void SetVolume(ushort level)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public override void MuteToggle()
|
||||
// {
|
||||
// }
|
||||
|
||||
// public override void VolumeDown(bool pressRelease)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public override void VolumeUp(bool pressRelease)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
//}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Essentials.Devices.Common.Codec;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
public abstract class DspBase : Device
|
||||
{
|
||||
public Dictionary<string, DspControlPoint> LevelControlPoints { get; private set; }
|
||||
|
||||
public Dictionary<string, DspControlPoint> DialerControlPoints { get; private set; }
|
||||
|
||||
public Dictionary<string, DspControlPoint> SwitcherControlPoints { get; private set; }
|
||||
|
||||
public abstract void RunPreset(string name);
|
||||
|
||||
public DspBase(string key, string name) :
|
||||
base(key, name) { }
|
||||
|
||||
|
||||
// in audio call feedback
|
||||
|
||||
// VOIP
|
||||
// Phone dialer
|
||||
}
|
||||
|
||||
// Fusion
|
||||
// Privacy state
|
||||
// Online state
|
||||
// level/mutes ?
|
||||
|
||||
// AC Log call stats
|
||||
|
||||
// Typical presets:
|
||||
// call default preset to restore levels and mutes
|
||||
|
||||
public abstract class DspControlPoint
|
||||
{
|
||||
string Key { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class DspDialerBase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Main program
|
||||
// VTC
|
||||
// ATC
|
||||
// Mics, unusual
|
||||
|
||||
public interface IBiampTesiraDspLevelControl : IBasicVolumeWithFeedback
|
||||
{
|
||||
/// <summary>
|
||||
/// In BiAmp: Instance Tag, QSC: Named Control, Polycom:
|
||||
/// </summary>
|
||||
string ControlPointTag { get; }
|
||||
#warning I dont think index1 and index2 should be a part of the interface. JTA 2018-07-17
|
||||
int Index1 { get; }
|
||||
int Index2 { get; }
|
||||
bool HasMute { get; }
|
||||
bool HasLevel { get; }
|
||||
bool AutomaticUnmuteOnVolumeUp { get; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using PepperDash.Essentials.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
public class SoundStructureBasics
|
||||
{
|
||||
// public Dictionary<string, IBiampTesiraDspLevelControl> Levels { get; private set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Devices.Common.Codec;
|
||||
using System.Text.RegularExpressions;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
|
||||
// QUESTIONS:
|
||||
//
|
||||
// When subscribing, just use the Instance ID for Custom Name?
|
||||
|
||||
// Verbose on subscriptions?
|
||||
|
||||
// Example subscription feedback responses
|
||||
// ! "publishToken":"name" "value":-77.0
|
||||
// ! "myLevelName" -77
|
||||
|
||||
public class QscDsp : DspBase
|
||||
{
|
||||
public IBasicCommunication Communication { get; private set; }
|
||||
public CommunicationGather PortGather { get; private set; }
|
||||
public GenericCommunicationMonitor CommunicationMonitor { get; private set; }
|
||||
|
||||
new public Dictionary<string, QscDspLevelControl> LevelControlPoints { get; private set; }
|
||||
new public Dictionary<string, QscDspDialer> Dialers { get; set; }
|
||||
public List<QscDspPresets> PresetList = new List<QscDspPresets>();
|
||||
|
||||
public bool isSubscribed;
|
||||
|
||||
|
||||
|
||||
CrestronQueue CommandQueue;
|
||||
|
||||
bool CommandQueueInProgress = false;
|
||||
|
||||
|
||||
public bool ShowHexResponse { get; set; }
|
||||
|
||||
public QscDsp(string key, string name, IBasicCommunication comm, QscDspPropertiesConfig props) :
|
||||
base(key, name)
|
||||
{
|
||||
CommandQueue = new CrestronQueue(100);
|
||||
|
||||
Communication = comm;
|
||||
var socket = comm as ISocketStatus;
|
||||
if (socket != null)
|
||||
{
|
||||
// This instance uses IP control
|
||||
socket.ConnectionChange += new EventHandler<GenericSocketStatusChageEventArgs>(socket_ConnectionChange);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This instance uses RS-232 control
|
||||
}
|
||||
PortGather = new CommunicationGather(Communication, "\x0a");
|
||||
PortGather.LineReceived += this.Port_LineReceived;
|
||||
|
||||
LevelControlPoints = new Dictionary<string, QscDspLevelControl>();
|
||||
Dialers = new Dictionary<string, QscDspDialer>();
|
||||
|
||||
if (props.CommunicationMonitorProperties != null)
|
||||
{
|
||||
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 20000, 120000, 300000, "cgp 1\x0D\x0A");
|
||||
}
|
||||
|
||||
|
||||
foreach (KeyValuePair<string, QscDspLevelControlBlockConfig> block in props.LevelControlBlocks)
|
||||
{
|
||||
this.LevelControlPoints.Add(block.Key, new QscDspLevelControl(block.Key, block.Value, this));
|
||||
Debug.Console(2, this, "Added LevelControlPoint {0}", block.Key);
|
||||
|
||||
|
||||
}
|
||||
foreach (KeyValuePair<string, QscDspPresets> preset in props.presets)
|
||||
{
|
||||
this.addPreset(preset.Value);
|
||||
Debug.Console(2, this, "Added Preset {0} {1}", preset.Value.label, preset.Value.preset);
|
||||
}
|
||||
foreach (KeyValuePair<string, QscDialerConfig> dialerConfig in props.dialerControlBlocks)
|
||||
{
|
||||
Debug.Console(2, this, "Added Dialer {0}\n {1}", dialerConfig.Key, dialerConfig.Value);
|
||||
this.Dialers.Add(dialerConfig.Key, new QscDspDialer(dialerConfig.Value, this));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override bool CustomActivate()
|
||||
{
|
||||
Communication.Connect();
|
||||
CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); };
|
||||
CommunicationMonitor.Start();
|
||||
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
return true;
|
||||
}
|
||||
|
||||
void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)
|
||||
{
|
||||
Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString());
|
||||
|
||||
if (e.Client.IsConnected)
|
||||
{
|
||||
SubscribeToAttributes();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cleanup items from this session
|
||||
CommandQueue.Clear();
|
||||
CommandQueueInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the subscription process to the DSP
|
||||
/// </summary>
|
||||
void SubscribeToAttributes()
|
||||
{
|
||||
SendLine("cgd 1");
|
||||
|
||||
SendLine("cgc 1");
|
||||
|
||||
foreach (KeyValuePair<string, QscDspLevelControl> level in LevelControlPoints)
|
||||
{
|
||||
level.Value.Subscribe();
|
||||
}
|
||||
|
||||
foreach (var dialer in Dialers)
|
||||
{
|
||||
dialer.Value.Subscribe();
|
||||
}
|
||||
|
||||
if (CommunicationMonitor != null)
|
||||
{
|
||||
|
||||
CommunicationMonitor.Start();
|
||||
//CommunicationMonitor = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
//CommunicationMonitor.Message = "cgp 1\x0D\x0A";
|
||||
CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); };
|
||||
//CommunicationMonitor.Start();
|
||||
if (!CommandQueueInProgress)
|
||||
SendNextQueuedCommand();
|
||||
|
||||
// ResetSubscriptionTimer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handles a response message from the DSP
|
||||
/// </summary>
|
||||
/// <param name="dev"></param>
|
||||
/// <param name="args"></param>
|
||||
void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args)
|
||||
{
|
||||
Debug.Console(2, this, "RX: '{0}'", args.Text);
|
||||
try
|
||||
{
|
||||
if (args.Text.IndexOf("sr ") > -1)
|
||||
{
|
||||
}
|
||||
else if (args.Text.IndexOf("cv") > -1)
|
||||
{
|
||||
|
||||
var changeMessage = args.Text.Split(null);
|
||||
|
||||
string changedInstance = changeMessage[1].Replace("\"", "");
|
||||
Debug.Console(1, this, "cv parse Instance: {0}", changedInstance);
|
||||
bool foundItFlag = false;
|
||||
foreach (KeyValuePair<string, QscDspLevelControl> controlPoint in LevelControlPoints)
|
||||
{
|
||||
if (changedInstance == controlPoint.Value.LevelInstanceTag)
|
||||
{
|
||||
controlPoint.Value.ParseSubscriptionMessage(changedInstance, changeMessage[4]);
|
||||
foundItFlag = true;
|
||||
return;
|
||||
}
|
||||
else if (changedInstance == controlPoint.Value.MuteInstanceTag)
|
||||
{
|
||||
controlPoint.Value.ParseSubscriptionMessage(changedInstance, changeMessage[2].Replace("\"", ""));
|
||||
foundItFlag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
if (!foundItFlag)
|
||||
{
|
||||
foreach (var dialer in Dialers)
|
||||
{
|
||||
PropertyInfo[] properties = dialer.Value.Tags.GetType().GetCType().GetProperties();
|
||||
//GetPropertyValues(Tags);
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
var propValue = prop.GetValue(dialer.Value.Tags, null) as string;
|
||||
if (changedInstance == propValue)
|
||||
{
|
||||
dialer.Value.ParseSubscriptionMessage(changedInstance, changeMessage[2].Replace("\"", ""));
|
||||
foundItFlag = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (foundItFlag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Debug.Level == 2)
|
||||
Debug.Console(2, this, "Error parsing response: '{0}'\n{1}", args.Text, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a command to the DSP (with delimiter appended)
|
||||
/// </summary>
|
||||
/// <param name="s">Command to send</param>
|
||||
public void SendLine(string s)
|
||||
{
|
||||
Debug.Console(1, this, "TX: '{0}'", s);
|
||||
Communication.SendText(s + "\x0a");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command from a child module to the queue
|
||||
/// </summary>
|
||||
/// <param name="command">Command object from child module</param>
|
||||
public void EnqueueCommand(QueuedCommand commandToEnqueue)
|
||||
{
|
||||
CommandQueue.Enqueue(commandToEnqueue);
|
||||
//Debug.Console(1, this, "Command (QueuedCommand) Enqueued '{0}'. CommandQueue has '{1}' Elements.", commandToEnqueue.Command, CommandQueue.Count);
|
||||
|
||||
if(!CommandQueueInProgress)
|
||||
SendNextQueuedCommand();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a raw string command to the queue
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
public void EnqueueCommand(string command)
|
||||
{
|
||||
CommandQueue.Enqueue(command);
|
||||
//Debug.Console(1, this, "Command (string) Enqueued '{0}'. CommandQueue has '{1}' Elements.", command, CommandQueue.Count);
|
||||
|
||||
if (!CommandQueueInProgress)
|
||||
SendNextQueuedCommand();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the next queued command to the DSP
|
||||
/// </summary>
|
||||
void SendNextQueuedCommand()
|
||||
{
|
||||
if (Communication.IsConnected && !CommandQueue.IsEmpty)
|
||||
{
|
||||
CommandQueueInProgress = true;
|
||||
|
||||
if (CommandQueue.Peek() is QueuedCommand)
|
||||
{
|
||||
QueuedCommand nextCommand = new QueuedCommand();
|
||||
|
||||
nextCommand = (QueuedCommand)CommandQueue.Peek();
|
||||
|
||||
SendLine(nextCommand.Command);
|
||||
}
|
||||
else
|
||||
{
|
||||
string nextCommand = (string)CommandQueue.Peek();
|
||||
|
||||
SendLine(nextCommand);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void RunPresetNumber(ushort n)
|
||||
{
|
||||
RunPreset(PresetList[n].preset);
|
||||
}
|
||||
|
||||
public void addPreset(QscDspPresets s)
|
||||
{
|
||||
PresetList.Add(s);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sends a command to execute a preset
|
||||
/// </summary>
|
||||
/// <param name="name">Preset Name</param>
|
||||
public override void RunPreset(string name)
|
||||
{
|
||||
SendLine(string.Format("ssl {0}", name));
|
||||
SendLine("cgp 1");
|
||||
}
|
||||
|
||||
public class QueuedCommand
|
||||
{
|
||||
public string Command { get; set; }
|
||||
public string AttributeCode { get; set; }
|
||||
public QscDspControlPoint ControlPoint { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
public abstract class QscDspControlPoint : DspControlPoint
|
||||
{
|
||||
public string Key { get; protected set; }
|
||||
|
||||
public string LevelInstanceTag { get; set; }
|
||||
public string MuteInstanceTag { get; set; }
|
||||
public QscDsp Parent { get; private set; }
|
||||
|
||||
public bool IsSubscribed { get; protected set; }
|
||||
|
||||
protected QscDspControlPoint(string levelInstanceTag, string muteInstanceTag, QscDsp parent)
|
||||
{
|
||||
LevelInstanceTag = levelInstanceTag;
|
||||
MuteInstanceTag = muteInstanceTag;
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
virtual public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a command to the DSP
|
||||
/// </summary>
|
||||
/// <param name="command">command</param>
|
||||
/// <param name="attribute">attribute code</param>
|
||||
/// <param name="value">value (use "" if not applicable)</param>
|
||||
public virtual void SendFullCommand(string cmd, string instance, string value)
|
||||
{
|
||||
|
||||
var cmdToSemd = string.Format("{0} {1} {2}", cmd, instance, value);
|
||||
|
||||
Parent.SendLine(cmdToSemd);
|
||||
|
||||
}
|
||||
|
||||
virtual public void ParseGetMessage(string attributeCode, string message)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
public virtual void SendSubscriptionCommand(string instanceTag, string changeGroup)
|
||||
{
|
||||
// Subscription string format: InstanceTag subscribe attributeCode Index1 customName responseRate
|
||||
// Ex: "RoomLevel subscribe level 1 MyRoomLevel 500"
|
||||
|
||||
string cmd;
|
||||
|
||||
cmd = string.Format("cga {0} {1}", changeGroup, instanceTag);
|
||||
|
||||
Parent.SendLine(cmd);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Essentials.Devices.Common.Codec;
|
||||
using Crestron.SimplSharpPro.CrestronThread;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
public class QscDspDialer : DspDialerBase, IHasDialer
|
||||
{
|
||||
public QscDialerConfig Tags;
|
||||
public bool IsInCall { get; private set; }
|
||||
public QscDsp Parent { get; private set; }
|
||||
public string DialString { get; private set; }
|
||||
public bool OffHook { get; private set; }
|
||||
public bool AutoAnswerState { get; private set; }
|
||||
public bool DoNotDisturbState { get; private set; }
|
||||
|
||||
public BoolFeedback OffHookFeedback;
|
||||
public BoolFeedback AutoAnswerFeedback;
|
||||
public BoolFeedback DoNotDisturbFeedback;
|
||||
public StringFeedback DialStringFeedback;
|
||||
|
||||
// Add requirements for Dialer functionality
|
||||
|
||||
public QscDspDialer(QscDialerConfig Config, QscDsp parent)
|
||||
{
|
||||
Tags = Config;
|
||||
Parent = parent;
|
||||
DialStringFeedback = new StringFeedback(() => { return DialString; });
|
||||
OffHookFeedback = new BoolFeedback(() => { return OffHook; });
|
||||
AutoAnswerFeedback = new BoolFeedback(() => { return AutoAnswerState; });
|
||||
DoNotDisturbFeedback = new BoolFeedback(() => { return DoNotDisturbState; });
|
||||
}
|
||||
|
||||
public event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange;
|
||||
|
||||
public void Subscribe()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Do subscriptions and blah blah
|
||||
// This would be better using reflection JTA 2018-08-28
|
||||
PropertyInfo[] properties = Tags.GetType().GetCType().GetProperties();
|
||||
//GetPropertyValues(Tags);
|
||||
|
||||
Debug.Console(2, "QscDspDialer Subscribe");
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
//var val = prop.GetValue(obj, null);
|
||||
|
||||
|
||||
Debug.Console(2, "Property {0}, {1}, {2}\n", prop.GetType().Name, prop.Name, prop.PropertyType.FullName);
|
||||
if (prop.Name.Contains("Tag") && !prop.Name.Contains("keypad"))
|
||||
{
|
||||
var propValue = prop.GetValue(Tags, null) as string;
|
||||
Debug.Console(2, "Property {0}, {1}, {2}\n", prop.GetType().Name, prop.Name, propValue);
|
||||
SendSubscriptionCommand(propValue, "1");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
Debug.Console(2, "QscDspDialer Subscription Error: '{0}'\n", e);
|
||||
}
|
||||
|
||||
|
||||
// SendSubscriptionCommand(, "1");
|
||||
// SendSubscriptionCommand(config. , "mute", 500);
|
||||
}
|
||||
public void ParseSubscriptionMessage(string customName, string value)
|
||||
{
|
||||
|
||||
// Check for valid subscription response
|
||||
Debug.Console(1, "QscDialerTag {0} Response: '{1}'", customName, value);
|
||||
if (customName == Tags.dialStringTag)
|
||||
{
|
||||
Debug.Console(2, "QscDialerTag DialStringChanged ", value);
|
||||
this.DialString = value;
|
||||
this.DialStringFeedback.FireUpdate();
|
||||
}
|
||||
else if (customName == Tags.doNotDisturbTag)
|
||||
{
|
||||
if (value == "on")
|
||||
{
|
||||
this.DoNotDisturbState = true;
|
||||
}
|
||||
else if (value == "off")
|
||||
{
|
||||
this.DoNotDisturbState = false;
|
||||
}
|
||||
DoNotDisturbFeedback.FireUpdate();
|
||||
}
|
||||
else if (customName == Tags.callStatusTag)
|
||||
{
|
||||
if (value == "Dialing")
|
||||
{
|
||||
this.OffHook = true;
|
||||
}
|
||||
else if (value == "Disconnected")
|
||||
{
|
||||
this.OffHook = false;
|
||||
if (Tags.ClearOnHangup)
|
||||
{
|
||||
this.SendKeypad(eKeypadKeys.Clear);
|
||||
}
|
||||
}
|
||||
this.OffHookFeedback.FireUpdate();
|
||||
}
|
||||
else if (customName == Tags.autoAnswerTag)
|
||||
{
|
||||
if (value == "on")
|
||||
{
|
||||
this.AutoAnswerState = true;
|
||||
}
|
||||
else if (value == "off")
|
||||
{
|
||||
this.AutoAnswerState = false;
|
||||
}
|
||||
AutoAnswerFeedback.FireUpdate();
|
||||
}
|
||||
else if (customName == Tags.hookStatusTag)
|
||||
{
|
||||
if (value == "true")
|
||||
{
|
||||
this.OffHook = true;
|
||||
}
|
||||
else if (value == "false")
|
||||
{
|
||||
this.OffHook = false;
|
||||
}
|
||||
this.OffHookFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void DoNotDisturbToggle()
|
||||
{
|
||||
int dndStateInt = !DoNotDisturbState ? 1 : 0;
|
||||
Parent.SendLine(string.Format("csv {0} {1}", Tags.doNotDisturbTag, dndStateInt));
|
||||
}
|
||||
public void DoNotDisturbOn()
|
||||
{
|
||||
Parent.SendLine(string.Format("csv {0} 1", Tags.doNotDisturbTag));
|
||||
}
|
||||
public void DoNotDisturbOff()
|
||||
{
|
||||
Parent.SendLine(string.Format("csv {0} 0", Tags.doNotDisturbTag));
|
||||
}
|
||||
public void AutoAnswerToggle()
|
||||
{
|
||||
int autoAnswerStateInt = !AutoAnswerState ? 1 : 0;
|
||||
Parent.SendLine(string.Format("csv {0} {1}", Tags.autoAnswerTag, autoAnswerStateInt));
|
||||
}
|
||||
public void AutoAnswerOn()
|
||||
{
|
||||
Parent.SendLine(string.Format("csv {0} 1", Tags.autoAnswerTag));
|
||||
}
|
||||
public void AutoAnswerOff()
|
||||
{
|
||||
Parent.SendLine(string.Format("csv {0} 0", Tags.autoAnswerTag));
|
||||
}
|
||||
|
||||
private void PollKeypad()
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
Parent.SendLine(string.Format("cg {0}", Tags.dialStringTag));
|
||||
}
|
||||
|
||||
public void SendKeypad(eKeypadKeys button)
|
||||
{
|
||||
string keypadTag = null;
|
||||
switch (button)
|
||||
{
|
||||
case eKeypadKeys.Num0: keypadTag = Tags.keypad0Tag; break;
|
||||
case eKeypadKeys.Num1: keypadTag = Tags.keypad1Tag; break;
|
||||
case eKeypadKeys.Num2: keypadTag = Tags.keypad2Tag; break;
|
||||
case eKeypadKeys.Num3: keypadTag = Tags.keypad3Tag; break;
|
||||
case eKeypadKeys.Num4: keypadTag = Tags.keypad4Tag; break;
|
||||
case eKeypadKeys.Num5: keypadTag = Tags.keypad5Tag; break;
|
||||
case eKeypadKeys.Num6: keypadTag = Tags.keypad6Tag; break;
|
||||
case eKeypadKeys.Num7: keypadTag = Tags.keypad7Tag; break;
|
||||
case eKeypadKeys.Num8: keypadTag = Tags.keypad8Tag; break;
|
||||
case eKeypadKeys.Num9: keypadTag = Tags.keypad9Tag; break;
|
||||
case eKeypadKeys.Pound: keypadTag = Tags.keypadPoundTag; break;
|
||||
case eKeypadKeys.Star: keypadTag = Tags.keypadStarTag; break;
|
||||
case eKeypadKeys.Backspace: keypadTag = Tags.keypadBackspaceTag; break;
|
||||
case eKeypadKeys.Clear: keypadTag = Tags.keypadClearTag; break;
|
||||
}
|
||||
if (keypadTag != null)
|
||||
{
|
||||
var cmdToSend = string.Format("ct {0}", keypadTag);
|
||||
Parent.SendLine(cmdToSend);
|
||||
PollKeypad();
|
||||
}
|
||||
}
|
||||
public void SendSubscriptionCommand(string instanceTag, string changeGroup)
|
||||
{
|
||||
// Subscription string format: InstanceTag subscribe attributeCode Index1 customName responseRate
|
||||
// Ex: "RoomLevel subscribe level 1 MyRoomLevel 500"
|
||||
|
||||
var cmd = string.Format("cga {0} {1}", changeGroup, instanceTag);
|
||||
|
||||
Parent.SendLine(cmd);
|
||||
}
|
||||
public void Dial()
|
||||
{
|
||||
if (!this.OffHook)
|
||||
{
|
||||
Parent.SendLine(string.Format("ct {0}", Tags.connectTag));
|
||||
}
|
||||
else
|
||||
{
|
||||
Parent.SendLine(string.Format("ct {0}", Tags.disconnectTag));
|
||||
}
|
||||
Thread.Sleep(50);
|
||||
Parent.SendLine(string.Format("cg {0}", Tags.callStatusTag));
|
||||
}
|
||||
public void Dial(string number)
|
||||
{
|
||||
}
|
||||
public void EndCall(CodecActiveCallItem activeCall)
|
||||
{
|
||||
}
|
||||
public void EndAllCalls()
|
||||
{
|
||||
}
|
||||
public void AcceptCall(CodecActiveCallItem item)
|
||||
{
|
||||
}
|
||||
|
||||
public void RejectCall(CodecActiveCallItem item)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void SendDtmf(string digit)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public enum eKeypadKeys
|
||||
{
|
||||
Num1,
|
||||
Num2,
|
||||
Num3,
|
||||
Num4,
|
||||
Num5,
|
||||
Num6,
|
||||
Num7,
|
||||
Num8,
|
||||
Num9,
|
||||
Num0,
|
||||
Star,
|
||||
Pound,
|
||||
Clear,
|
||||
Backspace
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
public class QscDspLevelControl : QscDspControlPoint, IBasicVolumeWithFeedback, IKeyed
|
||||
{
|
||||
bool _IsMuted;
|
||||
ushort _VolumeLevel;
|
||||
|
||||
public BoolFeedback MuteFeedback { get; private set; }
|
||||
|
||||
public IntFeedback VolumeLevelFeedback { get; private set; }
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public ePdtLevelTypes Type;
|
||||
CTimer VolumeUpRepeatTimer;
|
||||
CTimer VolumeDownRepeatTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Used for to identify level subscription values
|
||||
/// </summary>
|
||||
public string LevelCustomName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used for to identify mute subscription values
|
||||
/// </summary>
|
||||
public string MuteCustomName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum fader level
|
||||
/// </summary>
|
||||
double MinLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum fader level
|
||||
/// </summary>
|
||||
double MaxLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a valid subscription string has been recieved for all subscriptions
|
||||
/// </summary>
|
||||
public bool IsSubsribed
|
||||
{
|
||||
get
|
||||
{
|
||||
bool isSubscribed = false;
|
||||
|
||||
if (HasMute && MuteIsSubscribed)
|
||||
isSubscribed = true;
|
||||
|
||||
if (HasLevel && LevelIsSubscribed)
|
||||
isSubscribed = true;
|
||||
|
||||
return isSubscribed;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AutomaticUnmuteOnVolumeUp { get; private set; }
|
||||
|
||||
public bool HasMute { get; private set; }
|
||||
|
||||
public bool HasLevel { get; private set; }
|
||||
|
||||
bool MuteIsSubscribed;
|
||||
|
||||
bool LevelIsSubscribed;
|
||||
|
||||
//public TesiraForteLevelControl(string label, string id, int index1, int index2, bool hasMute, bool hasLevel, BiampTesiraForteDsp parent)
|
||||
// : base(id, index1, index2, parent)
|
||||
//{
|
||||
// Initialize(label, hasMute, hasLevel);
|
||||
//}
|
||||
|
||||
public QscDspLevelControl(string key, QscDspLevelControlBlockConfig config, QscDsp parent)
|
||||
: base(config.LevelInstanceTag, config.MuteInstanceTag, parent)
|
||||
{
|
||||
if (!config.Disabled)
|
||||
{
|
||||
Initialize(key, config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this attribute based on config values and generates subscriptions commands and adds commands to the parent's queue.
|
||||
/// </summary>
|
||||
public void Initialize(string key, QscDspLevelControlBlockConfig config)
|
||||
{
|
||||
Key = string.Format("{0}--{1}", Parent.Key, key);
|
||||
Enabled = true;
|
||||
DeviceManager.AddDevice(this);
|
||||
if (config.IsMic)
|
||||
{
|
||||
Type = ePdtLevelTypes.microphone;
|
||||
}
|
||||
else
|
||||
{
|
||||
Type = ePdtLevelTypes.speaker;
|
||||
}
|
||||
|
||||
Debug.Console(2, this, "Adding LevelControl '{0}'", Key);
|
||||
|
||||
this.IsSubscribed = false;
|
||||
|
||||
MuteFeedback = new BoolFeedback(() => _IsMuted);
|
||||
|
||||
VolumeLevelFeedback = new IntFeedback(() => _VolumeLevel);
|
||||
|
||||
VolumeUpRepeatTimer = new CTimer(VolumeUpRepeat, Timeout.Infinite);
|
||||
VolumeDownRepeatTimer = new CTimer(VolumeDownRepeat, Timeout.Infinite);
|
||||
LevelCustomName = config.Label;
|
||||
HasMute = config.HasMute;
|
||||
HasLevel = config.HasLevel;
|
||||
}
|
||||
|
||||
public void Subscribe()
|
||||
{
|
||||
// Do subscriptions and blah blah
|
||||
|
||||
// Subscribe to mute
|
||||
if (this.HasMute)
|
||||
{
|
||||
|
||||
SendSubscriptionCommand(this.MuteInstanceTag, "1");
|
||||
// SendSubscriptionCommand(config. , "mute", 500);
|
||||
}
|
||||
|
||||
// Subscribe to level
|
||||
if (this.HasLevel)
|
||||
{
|
||||
|
||||
SendSubscriptionCommand(this.LevelInstanceTag, "1");
|
||||
// SendSubscriptionCommand(this.con, "level", 250);
|
||||
|
||||
//SendFullCommand("get", "minLevel", null);
|
||||
|
||||
//SendFullCommand("get", "maxLevel", null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Parses the response from the DspBase
|
||||
/// </summary>
|
||||
/// <param name="customName"></param>
|
||||
/// <param name="value"></param>
|
||||
public void ParseSubscriptionMessage(string customName, string value)
|
||||
{
|
||||
|
||||
// Check for valid subscription response
|
||||
Debug.Console(1, this, "Level {0} Response: '{1}'", customName, value);
|
||||
if (customName == MuteInstanceTag)
|
||||
{
|
||||
if (value == "muted")
|
||||
{
|
||||
_IsMuted = true;
|
||||
MuteIsSubscribed = true;
|
||||
|
||||
}
|
||||
else if (value == "unmuted")
|
||||
{
|
||||
_IsMuted = false;
|
||||
MuteIsSubscribed = true;
|
||||
}
|
||||
|
||||
MuteFeedback.FireUpdate();
|
||||
}
|
||||
else if (customName == LevelInstanceTag)
|
||||
{
|
||||
|
||||
|
||||
var _value = Double.Parse(value);
|
||||
|
||||
_VolumeLevel = (ushort)(_value * 65535);
|
||||
Debug.Console(1, this, "Level {0} VolumeLevel: '{1}'", customName, _VolumeLevel);
|
||||
LevelIsSubscribed = true;
|
||||
|
||||
VolumeLevelFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Turns the mute off
|
||||
/// </summary>
|
||||
public void MuteOff()
|
||||
{
|
||||
SendFullCommand("csv", this.MuteInstanceTag, "0");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns the mute on
|
||||
/// </summary>
|
||||
public void MuteOn()
|
||||
{
|
||||
SendFullCommand("csv", this.MuteInstanceTag, "1");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the volume to a specified level
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
public void SetVolume(ushort level)
|
||||
{
|
||||
Debug.Console(1, this, "volume: {0}", level);
|
||||
// Unmute volume if new level is higher than existing
|
||||
if (AutomaticUnmuteOnVolumeUp && _IsMuted)
|
||||
{
|
||||
MuteOff();
|
||||
}
|
||||
double newLevel = Scale(level);
|
||||
Debug.Console(1, this, "newVolume: {0}", newLevel);
|
||||
SendFullCommand("csp", this.LevelInstanceTag, string.Format("{0}", newLevel));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles mute status
|
||||
/// </summary>
|
||||
public void MuteToggle()
|
||||
{
|
||||
|
||||
if (_IsMuted)
|
||||
{
|
||||
SendFullCommand("csv", this.MuteInstanceTag, "0");
|
||||
}
|
||||
else
|
||||
{
|
||||
SendFullCommand("csv", this.MuteInstanceTag, "1");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void VolumeUpRepeat(object callbackObject)
|
||||
{
|
||||
this.VolumeUp(true);
|
||||
}
|
||||
public void VolumeDownRepeat(object callbackObject)
|
||||
{
|
||||
this.VolumeDown(true);
|
||||
}
|
||||
|
||||
public void VolumeDown(bool press)
|
||||
{
|
||||
|
||||
|
||||
if (press)
|
||||
{
|
||||
VolumeDownRepeatTimer.Reset(100);
|
||||
SendFullCommand("css ", this.LevelInstanceTag, "--");
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
VolumeDownRepeatTimer.Stop();
|
||||
// VolumeDownRepeatTimer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments volume level
|
||||
/// </summary>
|
||||
/// <param name="pressRelease"></param>
|
||||
public void VolumeUp(bool press)
|
||||
{
|
||||
if (press)
|
||||
{
|
||||
VolumeUpRepeatTimer.Reset(100);
|
||||
SendFullCommand("css ", this.LevelInstanceTag, "++");
|
||||
|
||||
if (AutomaticUnmuteOnVolumeUp)
|
||||
if (!_IsMuted)
|
||||
MuteOff();
|
||||
}
|
||||
else
|
||||
{
|
||||
VolumeUpRepeatTimer.Stop();
|
||||
}
|
||||
}
|
||||
/// <returns></returns>
|
||||
double Scale(double input)
|
||||
{
|
||||
Debug.Console(1, this, "Scaling (double) input '{0}'",input );
|
||||
|
||||
var output = (input / 65535);
|
||||
|
||||
Debug.Console(1, this, "Scaled output '{0}'", output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
public enum ePdtLevelTypes
|
||||
{
|
||||
speaker = 0,
|
||||
microphone = 1
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
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.Devices.Common.DSP
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class QscDspPropertiesConfig
|
||||
{
|
||||
public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; }
|
||||
|
||||
public ControlPropertiesConfig Control { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// These are key-value pairs, string id, string type.
|
||||
/// Valid types are level and mute.
|
||||
/// Need to include the index values somehow
|
||||
/// </summary>
|
||||
public Dictionary<string, QscDspLevelControlBlockConfig> LevelControlBlocks { get; set; }
|
||||
public Dictionary<string, QscDialerConfig> dialerControlBlocks { get; set; }
|
||||
public Dictionary<string, QscDspPresets> presets { get; set; }
|
||||
// public Dictionary<string, BiampTesiraForteDialerControlBlockConfig> DialerControlBlocks {get; set;}
|
||||
}
|
||||
public interface IQscDspBasicLevel : IBasicVolumeWithFeedback
|
||||
{
|
||||
/// <summary>
|
||||
/// In BiAmp: Instance Tag, QSC: Named Control, Polycom:
|
||||
/// </summary>
|
||||
string LevelInstanceTag { get; set; }
|
||||
string MuteInstanceTag { get; set; }
|
||||
bool HasMute { get; }
|
||||
bool HasLevel { get; }
|
||||
bool AutomaticUnmuteOnVolumeUp { get; }
|
||||
}
|
||||
public class QscDspLevelControlBlockConfig
|
||||
{
|
||||
public bool Disabled { get; set; }
|
||||
public string Label { get; set; }
|
||||
public string LevelInstanceTag { get; set; }
|
||||
public string MuteInstanceTag { get; set; }
|
||||
public bool HasMute { get; set; }
|
||||
public bool HasLevel { get; set; }
|
||||
public bool IsMic { get; set; }
|
||||
}
|
||||
|
||||
public class QscDialerConfig
|
||||
{
|
||||
public string incomingCallRingerTag {get; set;}
|
||||
public string dialStringTag {get; set;}
|
||||
public string disconnectTag {get; set;}
|
||||
public string connectTag {get; set;}
|
||||
public string callStatusTag {get; set;}
|
||||
public string hookStatusTag {get; set;}
|
||||
public string doNotDisturbTag { get; set; }
|
||||
public string autoAnswerTag { get; set; }
|
||||
|
||||
public string keypadBackspaceTag {get; set;}
|
||||
public string keypadClearTag {get; set;}
|
||||
public string keypad1Tag {get; set;}
|
||||
public string keypad2Tag {get; set;}
|
||||
public string keypad3Tag {get; set;}
|
||||
public string keypad4Tag {get; set;}
|
||||
public string keypad5Tag {get; set;}
|
||||
public string keypad6Tag {get; set;}
|
||||
public string keypad7Tag {get; set;}
|
||||
public string keypad8Tag {get; set;}
|
||||
public string keypad9Tag {get; set;}
|
||||
public string keypad0Tag {get; set;}
|
||||
public string keypadPoundTag {get; set;}
|
||||
public string keypadStarTag {get; set;}
|
||||
|
||||
public bool ClearOnHangup { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class QscDspPresets
|
||||
{
|
||||
public string label { get; set; }
|
||||
public string preset { get; set; }
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,328 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Devices.Common.Codec;
|
||||
using System.Text.RegularExpressions;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
|
||||
// QUESTIONS:
|
||||
//
|
||||
// When subscribing, just use the Instance ID for Custom Name?
|
||||
|
||||
// Verbose on subscriptions?
|
||||
|
||||
// Example subscription feedback responses
|
||||
// ! "publishToken":"name" "value":-77.0
|
||||
// ! "myLevelName" -77
|
||||
|
||||
public class QscDsp : DspBase
|
||||
{
|
||||
public IBasicCommunication Communication { get; private set; }
|
||||
public CommunicationGather PortGather { get; private set; }
|
||||
public GenericCommunicationMonitor CommunicationMonitor { get; private set; }
|
||||
|
||||
new public Dictionary<string, QscDspLevelControl> LevelControlPoints { get; private set; }
|
||||
new public Dictionary<string, QscDspDialer> Dialers { get; set; }
|
||||
public List<QscDspPresets> PresetList = new List<QscDspPresets>();
|
||||
|
||||
public bool isSubscribed;
|
||||
|
||||
|
||||
|
||||
CrestronQueue CommandQueue;
|
||||
|
||||
bool CommandQueueInProgress = false;
|
||||
|
||||
|
||||
public bool ShowHexResponse { get; set; }
|
||||
|
||||
public QscDsp(string key, string name, IBasicCommunication comm, QscDspPropertiesConfig props) :
|
||||
base(key, name)
|
||||
{
|
||||
CommandQueue = new CrestronQueue(100);
|
||||
|
||||
Communication = comm;
|
||||
var socket = comm as ISocketStatus;
|
||||
if (socket != null)
|
||||
{
|
||||
// This instance uses IP control
|
||||
socket.ConnectionChange += new EventHandler<GenericSocketStatusChageEventArgs>(socket_ConnectionChange);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This instance uses RS-232 control
|
||||
}
|
||||
PortGather = new CommunicationGather(Communication, "\x0a");
|
||||
PortGather.LineReceived += this.Port_LineReceived;
|
||||
|
||||
LevelControlPoints = new Dictionary<string, QscDspLevelControl>();
|
||||
Dialers = new Dictionary<string, QscDspDialer>();
|
||||
|
||||
if (props.CommunicationMonitorProperties != null)
|
||||
{
|
||||
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, props.CommunicationMonitorProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 20000, 120000, 300000, "cgp 1\x0D\x0A");
|
||||
}
|
||||
|
||||
|
||||
foreach (KeyValuePair<string, QscDspLevelControlBlockConfig> block in props.LevelControlBlocks)
|
||||
{
|
||||
this.LevelControlPoints.Add(block.Key, new QscDspLevelControl(block.Key, block.Value, this));
|
||||
Debug.Console(2, this, "Added LevelControlPoint {0}", block.Key);
|
||||
|
||||
|
||||
}
|
||||
foreach (KeyValuePair<string, QscDspPresets> preset in props.presets)
|
||||
{
|
||||
this.addPreset(preset.Value);
|
||||
Debug.Console(2, this, "Added Preset {0} {1}", preset.Value.label, preset.Value.preset);
|
||||
}
|
||||
foreach (KeyValuePair<string, QscDialerConfig> dialerConfig in props.dialerControlBlocks)
|
||||
{
|
||||
Debug.Console(2, this, "Added Dialer {0}\n {1}", dialerConfig.Key, dialerConfig.Value);
|
||||
this.Dialers.Add(dialerConfig.Key, new QscDspDialer(dialerConfig.Value, this));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override bool CustomActivate()
|
||||
{
|
||||
Communication.Connect();
|
||||
CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); };
|
||||
CommunicationMonitor.Start();
|
||||
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(SendLine, "send" + Key, "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(s => Communication.Connect(), "con" + Key, "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
return true;
|
||||
}
|
||||
|
||||
void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e)
|
||||
{
|
||||
Debug.Console(2, this, "Socket Status Change: {0}", e.Client.ClientStatus.ToString());
|
||||
|
||||
if (e.Client.IsConnected)
|
||||
{
|
||||
SubscribeToAttributes();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cleanup items from this session
|
||||
CommandQueue.Clear();
|
||||
CommandQueueInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the subscription process to the DSP
|
||||
/// </summary>
|
||||
void SubscribeToAttributes()
|
||||
{
|
||||
SendLine("cgd 1");
|
||||
|
||||
SendLine("cgc 1");
|
||||
|
||||
foreach (KeyValuePair<string, QscDspLevelControl> level in LevelControlPoints)
|
||||
{
|
||||
level.Value.Subscribe();
|
||||
}
|
||||
|
||||
foreach (var dialer in Dialers)
|
||||
{
|
||||
dialer.Value.Subscribe();
|
||||
}
|
||||
|
||||
if (CommunicationMonitor != null)
|
||||
{
|
||||
|
||||
CommunicationMonitor.Start();
|
||||
//CommunicationMonitor = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
//CommunicationMonitor.Message = "cgp 1\x0D\x0A";
|
||||
CommunicationMonitor.StatusChange += (o, a) => { Debug.Console(2, this, "Communication monitor state: {0}", CommunicationMonitor.Status); };
|
||||
//CommunicationMonitor.Start();
|
||||
if (!CommandQueueInProgress)
|
||||
SendNextQueuedCommand();
|
||||
|
||||
// ResetSubscriptionTimer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handles a response message from the DSP
|
||||
/// </summary>
|
||||
/// <param name="dev"></param>
|
||||
/// <param name="args"></param>
|
||||
void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args)
|
||||
{
|
||||
Debug.Console(2, this, "RX: '{0}'", args.Text);
|
||||
try
|
||||
{
|
||||
if (args.Text.IndexOf("sr ") > -1)
|
||||
{
|
||||
}
|
||||
else if (args.Text.IndexOf("cv") > -1)
|
||||
{
|
||||
|
||||
var changeMessage = args.Text.Split(null);
|
||||
|
||||
string changedInstance = changeMessage[1].Replace("\"", "");
|
||||
Debug.Console(1, this, "cv parse Instance: {0}", changedInstance);
|
||||
bool foundItFlag = false;
|
||||
foreach (KeyValuePair<string, QscDspLevelControl> controlPoint in LevelControlPoints)
|
||||
{
|
||||
if (changedInstance == controlPoint.Value.LevelInstanceTag)
|
||||
{
|
||||
controlPoint.Value.ParseSubscriptionMessage(changedInstance, changeMessage[4]);
|
||||
foundItFlag = true;
|
||||
return;
|
||||
}
|
||||
else if (changedInstance == controlPoint.Value.MuteInstanceTag)
|
||||
{
|
||||
controlPoint.Value.ParseSubscriptionMessage(changedInstance, changeMessage[2].Replace("\"", ""));
|
||||
foundItFlag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
if (!foundItFlag)
|
||||
{
|
||||
foreach (var dialer in Dialers)
|
||||
{
|
||||
PropertyInfo[] properties = dialer.Value.Tags.GetType().GetCType().GetProperties();
|
||||
//GetPropertyValues(Tags);
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
var propValue = prop.GetValue(dialer.Value.Tags, null) as string;
|
||||
if (changedInstance == propValue)
|
||||
{
|
||||
dialer.Value.ParseSubscriptionMessage(changedInstance, changeMessage[2].Replace("\"", ""));
|
||||
foundItFlag = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (foundItFlag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Debug.Level == 2)
|
||||
Debug.Console(2, this, "Error parsing response: '{0}'\n{1}", args.Text, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a command to the DSP (with delimiter appended)
|
||||
/// </summary>
|
||||
/// <param name="s">Command to send</param>
|
||||
public void SendLine(string s)
|
||||
{
|
||||
Debug.Console(1, this, "TX: '{0}'", s);
|
||||
Communication.SendText(s + "\x0a");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command from a child module to the queue
|
||||
/// </summary>
|
||||
/// <param name="command">Command object from child module</param>
|
||||
public void EnqueueCommand(QueuedCommand commandToEnqueue)
|
||||
{
|
||||
CommandQueue.Enqueue(commandToEnqueue);
|
||||
//Debug.Console(1, this, "Command (QueuedCommand) Enqueued '{0}'. CommandQueue has '{1}' Elements.", commandToEnqueue.Command, CommandQueue.Count);
|
||||
|
||||
if(!CommandQueueInProgress)
|
||||
SendNextQueuedCommand();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a raw string command to the queue
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
public void EnqueueCommand(string command)
|
||||
{
|
||||
CommandQueue.Enqueue(command);
|
||||
//Debug.Console(1, this, "Command (string) Enqueued '{0}'. CommandQueue has '{1}' Elements.", command, CommandQueue.Count);
|
||||
|
||||
if (!CommandQueueInProgress)
|
||||
SendNextQueuedCommand();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the next queued command to the DSP
|
||||
/// </summary>
|
||||
void SendNextQueuedCommand()
|
||||
{
|
||||
if (Communication.IsConnected && !CommandQueue.IsEmpty)
|
||||
{
|
||||
CommandQueueInProgress = true;
|
||||
|
||||
if (CommandQueue.Peek() is QueuedCommand)
|
||||
{
|
||||
QueuedCommand nextCommand = new QueuedCommand();
|
||||
|
||||
nextCommand = (QueuedCommand)CommandQueue.Peek();
|
||||
|
||||
SendLine(nextCommand.Command);
|
||||
}
|
||||
else
|
||||
{
|
||||
string nextCommand = (string)CommandQueue.Peek();
|
||||
|
||||
SendLine(nextCommand);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void RunPresetNumber(ushort n)
|
||||
{
|
||||
RunPreset(PresetList[n].preset);
|
||||
}
|
||||
|
||||
public void addPreset(QscDspPresets s)
|
||||
{
|
||||
PresetList.Add(s);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sends a command to execute a preset
|
||||
/// </summary>
|
||||
/// <param name="name">Preset Name</param>
|
||||
public override void RunPreset(string name)
|
||||
{
|
||||
SendLine(string.Format("ssl {0}", name));
|
||||
SendLine("cgp 1");
|
||||
}
|
||||
|
||||
public class QueuedCommand
|
||||
{
|
||||
public string Command { get; set; }
|
||||
public string AttributeCode { get; set; }
|
||||
public QscDspControlPoint ControlPoint { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
public abstract class QscDspControlPoint : DspControlPoint
|
||||
{
|
||||
public string Key { get; protected set; }
|
||||
|
||||
public string LevelInstanceTag { get; set; }
|
||||
public string MuteInstanceTag { get; set; }
|
||||
public QscDsp Parent { get; private set; }
|
||||
|
||||
public bool IsSubscribed { get; protected set; }
|
||||
|
||||
protected QscDspControlPoint(string levelInstanceTag, string muteInstanceTag, QscDsp parent)
|
||||
{
|
||||
LevelInstanceTag = levelInstanceTag;
|
||||
MuteInstanceTag = muteInstanceTag;
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
virtual public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a command to the DSP
|
||||
/// </summary>
|
||||
/// <param name="command">command</param>
|
||||
/// <param name="attribute">attribute code</param>
|
||||
/// <param name="value">value (use "" if not applicable)</param>
|
||||
public virtual void SendFullCommand(string cmd, string instance, string value)
|
||||
{
|
||||
|
||||
var cmdToSemd = string.Format("{0} {1} {2}", cmd, instance, value);
|
||||
|
||||
Parent.SendLine(cmdToSemd);
|
||||
|
||||
}
|
||||
|
||||
virtual public void ParseGetMessage(string attributeCode, string message)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
public virtual void SendSubscriptionCommand(string instanceTag, string changeGroup)
|
||||
{
|
||||
// Subscription string format: InstanceTag subscribe attributeCode Index1 customName responseRate
|
||||
// Ex: "RoomLevel subscribe level 1 MyRoomLevel 500"
|
||||
|
||||
string cmd;
|
||||
|
||||
cmd = string.Format("cga {0} {1}", changeGroup, instanceTag);
|
||||
|
||||
Parent.SendLine(cmd);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Essentials.Devices.Common.Codec;
|
||||
using Crestron.SimplSharpPro.CrestronThread;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
public class QscDspDialer : DspDialerBase, IHasDialer
|
||||
{
|
||||
public QscDialerConfig Tags;
|
||||
public bool IsInCall { get; private set; }
|
||||
public QscDsp Parent { get; private set; }
|
||||
public string DialString { get; private set; }
|
||||
public bool OffHook { get; private set; }
|
||||
public bool AutoAnswerState { get; private set; }
|
||||
public bool DoNotDisturbState { get; private set; }
|
||||
|
||||
public BoolFeedback OffHookFeedback;
|
||||
public BoolFeedback AutoAnswerFeedback;
|
||||
public BoolFeedback DoNotDisturbFeedback;
|
||||
public StringFeedback DialStringFeedback;
|
||||
|
||||
// Add requirements for Dialer functionality
|
||||
|
||||
public QscDspDialer(QscDialerConfig Config, QscDsp parent)
|
||||
{
|
||||
Tags = Config;
|
||||
Parent = parent;
|
||||
DialStringFeedback = new StringFeedback(() => { return DialString; });
|
||||
OffHookFeedback = new BoolFeedback(() => { return OffHook; });
|
||||
AutoAnswerFeedback = new BoolFeedback(() => { return AutoAnswerState; });
|
||||
DoNotDisturbFeedback = new BoolFeedback(() => { return DoNotDisturbState; });
|
||||
}
|
||||
|
||||
public event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange;
|
||||
|
||||
public void Subscribe()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Do subscriptions and blah blah
|
||||
// This would be better using reflection JTA 2018-08-28
|
||||
PropertyInfo[] properties = Tags.GetType().GetCType().GetProperties();
|
||||
//GetPropertyValues(Tags);
|
||||
|
||||
Debug.Console(2, "QscDspDialer Subscribe");
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
//var val = prop.GetValue(obj, null);
|
||||
|
||||
|
||||
Debug.Console(2, "Property {0}, {1}, {2}\n", prop.GetType().Name, prop.Name, prop.PropertyType.FullName);
|
||||
if (prop.Name.Contains("Tag") && !prop.Name.Contains("keypad"))
|
||||
{
|
||||
var propValue = prop.GetValue(Tags, null) as string;
|
||||
Debug.Console(2, "Property {0}, {1}, {2}\n", prop.GetType().Name, prop.Name, propValue);
|
||||
SendSubscriptionCommand(propValue, "1");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
Debug.Console(2, "QscDspDialer Subscription Error: '{0}'\n", e);
|
||||
}
|
||||
|
||||
|
||||
// SendSubscriptionCommand(, "1");
|
||||
// SendSubscriptionCommand(config. , "mute", 500);
|
||||
}
|
||||
public void ParseSubscriptionMessage(string customName, string value)
|
||||
{
|
||||
|
||||
// Check for valid subscription response
|
||||
Debug.Console(1, "QscDialerTag {0} Response: '{1}'", customName, value);
|
||||
if (customName == Tags.dialStringTag)
|
||||
{
|
||||
Debug.Console(2, "QscDialerTag DialStringChanged ", value);
|
||||
this.DialString = value;
|
||||
this.DialStringFeedback.FireUpdate();
|
||||
}
|
||||
else if (customName == Tags.doNotDisturbTag)
|
||||
{
|
||||
if (value == "on")
|
||||
{
|
||||
this.DoNotDisturbState = true;
|
||||
}
|
||||
else if (value == "off")
|
||||
{
|
||||
this.DoNotDisturbState = false;
|
||||
}
|
||||
DoNotDisturbFeedback.FireUpdate();
|
||||
}
|
||||
else if (customName == Tags.callStatusTag)
|
||||
{
|
||||
if (value == "Dialing")
|
||||
{
|
||||
this.OffHook = true;
|
||||
}
|
||||
else if (value == "Disconnected")
|
||||
{
|
||||
this.OffHook = false;
|
||||
if (Tags.ClearOnHangup)
|
||||
{
|
||||
this.SendKeypad(eKeypadKeys.Clear);
|
||||
}
|
||||
}
|
||||
this.OffHookFeedback.FireUpdate();
|
||||
}
|
||||
else if (customName == Tags.autoAnswerTag)
|
||||
{
|
||||
if (value == "on")
|
||||
{
|
||||
this.AutoAnswerState = true;
|
||||
}
|
||||
else if (value == "off")
|
||||
{
|
||||
this.AutoAnswerState = false;
|
||||
}
|
||||
AutoAnswerFeedback.FireUpdate();
|
||||
}
|
||||
else if (customName == Tags.hookStatusTag)
|
||||
{
|
||||
if (value == "true")
|
||||
{
|
||||
this.OffHook = true;
|
||||
}
|
||||
else if (value == "false")
|
||||
{
|
||||
this.OffHook = false;
|
||||
}
|
||||
this.OffHookFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void DoNotDisturbToggle()
|
||||
{
|
||||
int dndStateInt = !DoNotDisturbState ? 1 : 0;
|
||||
Parent.SendLine(string.Format("csv {0} {1}", Tags.doNotDisturbTag, dndStateInt));
|
||||
}
|
||||
public void DoNotDisturbOn()
|
||||
{
|
||||
Parent.SendLine(string.Format("csv {0} 1", Tags.doNotDisturbTag));
|
||||
}
|
||||
public void DoNotDisturbOff()
|
||||
{
|
||||
Parent.SendLine(string.Format("csv {0} 0", Tags.doNotDisturbTag));
|
||||
}
|
||||
public void AutoAnswerToggle()
|
||||
{
|
||||
int autoAnswerStateInt = !AutoAnswerState ? 1 : 0;
|
||||
Parent.SendLine(string.Format("csv {0} {1}", Tags.autoAnswerTag, autoAnswerStateInt));
|
||||
}
|
||||
public void AutoAnswerOn()
|
||||
{
|
||||
Parent.SendLine(string.Format("csv {0} 1", Tags.autoAnswerTag));
|
||||
}
|
||||
public void AutoAnswerOff()
|
||||
{
|
||||
Parent.SendLine(string.Format("csv {0} 0", Tags.autoAnswerTag));
|
||||
}
|
||||
|
||||
private void PollKeypad()
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
Parent.SendLine(string.Format("cg {0}", Tags.dialStringTag));
|
||||
}
|
||||
|
||||
public void SendKeypad(eKeypadKeys button)
|
||||
{
|
||||
string keypadTag = null;
|
||||
switch (button)
|
||||
{
|
||||
case eKeypadKeys.Num0: keypadTag = Tags.keypad0Tag; break;
|
||||
case eKeypadKeys.Num1: keypadTag = Tags.keypad1Tag; break;
|
||||
case eKeypadKeys.Num2: keypadTag = Tags.keypad2Tag; break;
|
||||
case eKeypadKeys.Num3: keypadTag = Tags.keypad3Tag; break;
|
||||
case eKeypadKeys.Num4: keypadTag = Tags.keypad4Tag; break;
|
||||
case eKeypadKeys.Num5: keypadTag = Tags.keypad5Tag; break;
|
||||
case eKeypadKeys.Num6: keypadTag = Tags.keypad6Tag; break;
|
||||
case eKeypadKeys.Num7: keypadTag = Tags.keypad7Tag; break;
|
||||
case eKeypadKeys.Num8: keypadTag = Tags.keypad8Tag; break;
|
||||
case eKeypadKeys.Num9: keypadTag = Tags.keypad9Tag; break;
|
||||
case eKeypadKeys.Pound: keypadTag = Tags.keypadPoundTag; break;
|
||||
case eKeypadKeys.Star: keypadTag = Tags.keypadStarTag; break;
|
||||
case eKeypadKeys.Backspace: keypadTag = Tags.keypadBackspaceTag; break;
|
||||
case eKeypadKeys.Clear: keypadTag = Tags.keypadClearTag; break;
|
||||
}
|
||||
if (keypadTag != null)
|
||||
{
|
||||
var cmdToSend = string.Format("ct {0}", keypadTag);
|
||||
Parent.SendLine(cmdToSend);
|
||||
PollKeypad();
|
||||
}
|
||||
}
|
||||
public void SendSubscriptionCommand(string instanceTag, string changeGroup)
|
||||
{
|
||||
// Subscription string format: InstanceTag subscribe attributeCode Index1 customName responseRate
|
||||
// Ex: "RoomLevel subscribe level 1 MyRoomLevel 500"
|
||||
|
||||
var cmd = string.Format("cga {0} {1}", changeGroup, instanceTag);
|
||||
|
||||
Parent.SendLine(cmd);
|
||||
}
|
||||
public void Dial()
|
||||
{
|
||||
if (!this.OffHook)
|
||||
{
|
||||
Parent.SendLine(string.Format("ct {0}", Tags.connectTag));
|
||||
}
|
||||
else
|
||||
{
|
||||
Parent.SendLine(string.Format("ct {0}", Tags.disconnectTag));
|
||||
}
|
||||
Thread.Sleep(50);
|
||||
Parent.SendLine(string.Format("cg {0}", Tags.callStatusTag));
|
||||
}
|
||||
public void Dial(string number)
|
||||
{
|
||||
}
|
||||
public void EndCall(CodecActiveCallItem activeCall)
|
||||
{
|
||||
}
|
||||
public void EndAllCalls()
|
||||
{
|
||||
}
|
||||
public void AcceptCall(CodecActiveCallItem item)
|
||||
{
|
||||
}
|
||||
|
||||
public void RejectCall(CodecActiveCallItem item)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void SendDtmf(string digit)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public enum eKeypadKeys
|
||||
{
|
||||
Num1,
|
||||
Num2,
|
||||
Num3,
|
||||
Num4,
|
||||
Num5,
|
||||
Num6,
|
||||
Num7,
|
||||
Num8,
|
||||
Num9,
|
||||
Num0,
|
||||
Star,
|
||||
Pound,
|
||||
Clear,
|
||||
Backspace
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
{
|
||||
public class QscDspLevelControl : QscDspControlPoint, IBasicVolumeWithFeedback, IKeyed
|
||||
{
|
||||
bool _IsMuted;
|
||||
ushort _VolumeLevel;
|
||||
|
||||
public BoolFeedback MuteFeedback { get; private set; }
|
||||
|
||||
public IntFeedback VolumeLevelFeedback { get; private set; }
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public ePdtLevelTypes Type;
|
||||
CTimer VolumeUpRepeatTimer;
|
||||
CTimer VolumeDownRepeatTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Used for to identify level subscription values
|
||||
/// </summary>
|
||||
public string LevelCustomName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used for to identify mute subscription values
|
||||
/// </summary>
|
||||
public string MuteCustomName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum fader level
|
||||
/// </summary>
|
||||
double MinLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum fader level
|
||||
/// </summary>
|
||||
double MaxLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a valid subscription string has been recieved for all subscriptions
|
||||
/// </summary>
|
||||
public bool IsSubsribed
|
||||
{
|
||||
get
|
||||
{
|
||||
bool isSubscribed = false;
|
||||
|
||||
if (HasMute && MuteIsSubscribed)
|
||||
isSubscribed = true;
|
||||
|
||||
if (HasLevel && LevelIsSubscribed)
|
||||
isSubscribed = true;
|
||||
|
||||
return isSubscribed;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AutomaticUnmuteOnVolumeUp { get; private set; }
|
||||
|
||||
public bool HasMute { get; private set; }
|
||||
|
||||
public bool HasLevel { get; private set; }
|
||||
|
||||
bool MuteIsSubscribed;
|
||||
|
||||
bool LevelIsSubscribed;
|
||||
|
||||
//public TesiraForteLevelControl(string label, string id, int index1, int index2, bool hasMute, bool hasLevel, BiampTesiraForteDsp parent)
|
||||
// : base(id, index1, index2, parent)
|
||||
//{
|
||||
// Initialize(label, hasMute, hasLevel);
|
||||
//}
|
||||
|
||||
public QscDspLevelControl(string key, QscDspLevelControlBlockConfig config, QscDsp parent)
|
||||
: base(config.LevelInstanceTag, config.MuteInstanceTag, parent)
|
||||
{
|
||||
if (!config.Disabled)
|
||||
{
|
||||
Initialize(key, config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this attribute based on config values and generates subscriptions commands and adds commands to the parent's queue.
|
||||
/// </summary>
|
||||
public void Initialize(string key, QscDspLevelControlBlockConfig config)
|
||||
{
|
||||
Key = string.Format("{0}--{1}", Parent.Key, key);
|
||||
Enabled = true;
|
||||
DeviceManager.AddDevice(this);
|
||||
if (config.IsMic)
|
||||
{
|
||||
Type = ePdtLevelTypes.microphone;
|
||||
}
|
||||
else
|
||||
{
|
||||
Type = ePdtLevelTypes.speaker;
|
||||
}
|
||||
|
||||
Debug.Console(2, this, "Adding LevelControl '{0}'", Key);
|
||||
|
||||
this.IsSubscribed = false;
|
||||
|
||||
MuteFeedback = new BoolFeedback(() => _IsMuted);
|
||||
|
||||
VolumeLevelFeedback = new IntFeedback(() => _VolumeLevel);
|
||||
|
||||
VolumeUpRepeatTimer = new CTimer(VolumeUpRepeat, Timeout.Infinite);
|
||||
VolumeDownRepeatTimer = new CTimer(VolumeDownRepeat, Timeout.Infinite);
|
||||
LevelCustomName = config.Label;
|
||||
HasMute = config.HasMute;
|
||||
HasLevel = config.HasLevel;
|
||||
}
|
||||
|
||||
public void Subscribe()
|
||||
{
|
||||
// Do subscriptions and blah blah
|
||||
|
||||
// Subscribe to mute
|
||||
if (this.HasMute)
|
||||
{
|
||||
|
||||
SendSubscriptionCommand(this.MuteInstanceTag, "1");
|
||||
// SendSubscriptionCommand(config. , "mute", 500);
|
||||
}
|
||||
|
||||
// Subscribe to level
|
||||
if (this.HasLevel)
|
||||
{
|
||||
|
||||
SendSubscriptionCommand(this.LevelInstanceTag, "1");
|
||||
// SendSubscriptionCommand(this.con, "level", 250);
|
||||
|
||||
//SendFullCommand("get", "minLevel", null);
|
||||
|
||||
//SendFullCommand("get", "maxLevel", null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Parses the response from the DspBase
|
||||
/// </summary>
|
||||
/// <param name="customName"></param>
|
||||
/// <param name="value"></param>
|
||||
public void ParseSubscriptionMessage(string customName, string value)
|
||||
{
|
||||
|
||||
// Check for valid subscription response
|
||||
Debug.Console(1, this, "Level {0} Response: '{1}'", customName, value);
|
||||
if (customName == MuteInstanceTag)
|
||||
{
|
||||
if (value == "muted")
|
||||
{
|
||||
_IsMuted = true;
|
||||
MuteIsSubscribed = true;
|
||||
|
||||
}
|
||||
else if (value == "unmuted")
|
||||
{
|
||||
_IsMuted = false;
|
||||
MuteIsSubscribed = true;
|
||||
}
|
||||
|
||||
MuteFeedback.FireUpdate();
|
||||
}
|
||||
else if (customName == LevelInstanceTag)
|
||||
{
|
||||
|
||||
|
||||
var _value = Double.Parse(value);
|
||||
|
||||
_VolumeLevel = (ushort)(_value * 65535);
|
||||
Debug.Console(1, this, "Level {0} VolumeLevel: '{1}'", customName, _VolumeLevel);
|
||||
LevelIsSubscribed = true;
|
||||
|
||||
VolumeLevelFeedback.FireUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Turns the mute off
|
||||
/// </summary>
|
||||
public void MuteOff()
|
||||
{
|
||||
SendFullCommand("csv", this.MuteInstanceTag, "0");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns the mute on
|
||||
/// </summary>
|
||||
public void MuteOn()
|
||||
{
|
||||
SendFullCommand("csv", this.MuteInstanceTag, "1");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the volume to a specified level
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
public void SetVolume(ushort level)
|
||||
{
|
||||
Debug.Console(1, this, "volume: {0}", level);
|
||||
// Unmute volume if new level is higher than existing
|
||||
if (AutomaticUnmuteOnVolumeUp && _IsMuted)
|
||||
{
|
||||
MuteOff();
|
||||
}
|
||||
double newLevel = Scale(level);
|
||||
Debug.Console(1, this, "newVolume: {0}", newLevel);
|
||||
SendFullCommand("csp", this.LevelInstanceTag, string.Format("{0}", newLevel));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles mute status
|
||||
/// </summary>
|
||||
public void MuteToggle()
|
||||
{
|
||||
|
||||
if (_IsMuted)
|
||||
{
|
||||
SendFullCommand("csv", this.MuteInstanceTag, "0");
|
||||
}
|
||||
else
|
||||
{
|
||||
SendFullCommand("csv", this.MuteInstanceTag, "1");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void VolumeUpRepeat(object callbackObject)
|
||||
{
|
||||
this.VolumeUp(true);
|
||||
}
|
||||
public void VolumeDownRepeat(object callbackObject)
|
||||
{
|
||||
this.VolumeDown(true);
|
||||
}
|
||||
|
||||
public void VolumeDown(bool press)
|
||||
{
|
||||
|
||||
|
||||
if (press)
|
||||
{
|
||||
VolumeDownRepeatTimer.Reset(100);
|
||||
SendFullCommand("css ", this.LevelInstanceTag, "--");
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
VolumeDownRepeatTimer.Stop();
|
||||
// VolumeDownRepeatTimer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments volume level
|
||||
/// </summary>
|
||||
/// <param name="pressRelease"></param>
|
||||
public void VolumeUp(bool press)
|
||||
{
|
||||
if (press)
|
||||
{
|
||||
VolumeUpRepeatTimer.Reset(100);
|
||||
SendFullCommand("css ", this.LevelInstanceTag, "++");
|
||||
|
||||
if (AutomaticUnmuteOnVolumeUp)
|
||||
if (!_IsMuted)
|
||||
MuteOff();
|
||||
}
|
||||
else
|
||||
{
|
||||
VolumeUpRepeatTimer.Stop();
|
||||
}
|
||||
}
|
||||
/// <returns></returns>
|
||||
double Scale(double input)
|
||||
{
|
||||
Debug.Console(1, this, "Scaling (double) input '{0}'",input );
|
||||
|
||||
var output = (input / 65535);
|
||||
|
||||
Debug.Console(1, this, "Scaled output '{0}'", output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
public enum ePdtLevelTypes
|
||||
{
|
||||
speaker = 0,
|
||||
microphone = 1
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
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.Devices.Common.DSP
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class QscDspPropertiesConfig
|
||||
{
|
||||
public CommunicationMonitorConfig CommunicationMonitorProperties { get; set; }
|
||||
|
||||
public ControlPropertiesConfig Control { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// These are key-value pairs, string id, string type.
|
||||
/// Valid types are level and mute.
|
||||
/// Need to include the index values somehow
|
||||
/// </summary>
|
||||
public Dictionary<string, QscDspLevelControlBlockConfig> LevelControlBlocks { get; set; }
|
||||
public Dictionary<string, QscDialerConfig> dialerControlBlocks { get; set; }
|
||||
public Dictionary<string, QscDspPresets> presets { get; set; }
|
||||
// public Dictionary<string, BiampTesiraForteDialerControlBlockConfig> DialerControlBlocks {get; set;}
|
||||
}
|
||||
public interface IQscDspBasicLevel : IBasicVolumeWithFeedback
|
||||
{
|
||||
/// <summary>
|
||||
/// In BiAmp: Instance Tag, QSC: Named Control, Polycom:
|
||||
/// </summary>
|
||||
string LevelInstanceTag { get; set; }
|
||||
string MuteInstanceTag { get; set; }
|
||||
bool HasMute { get; }
|
||||
bool HasLevel { get; }
|
||||
bool AutomaticUnmuteOnVolumeUp { get; }
|
||||
}
|
||||
public class QscDspLevelControlBlockConfig
|
||||
{
|
||||
public bool Disabled { get; set; }
|
||||
public string Label { get; set; }
|
||||
public string LevelInstanceTag { get; set; }
|
||||
public string MuteInstanceTag { get; set; }
|
||||
public bool HasMute { get; set; }
|
||||
public bool HasLevel { get; set; }
|
||||
public bool IsMic { get; set; }
|
||||
}
|
||||
|
||||
public class QscDialerConfig
|
||||
{
|
||||
public string incomingCallRingerTag {get; set;}
|
||||
public string dialStringTag {get; set;}
|
||||
public string disconnectTag {get; set;}
|
||||
public string connectTag {get; set;}
|
||||
public string callStatusTag {get; set;}
|
||||
public string hookStatusTag {get; set;}
|
||||
public string doNotDisturbTag { get; set; }
|
||||
public string autoAnswerTag { get; set; }
|
||||
|
||||
public string keypadBackspaceTag {get; set;}
|
||||
public string keypadClearTag {get; set;}
|
||||
public string keypad1Tag {get; set;}
|
||||
public string keypad2Tag {get; set;}
|
||||
public string keypad3Tag {get; set;}
|
||||
public string keypad4Tag {get; set;}
|
||||
public string keypad5Tag {get; set;}
|
||||
public string keypad6Tag {get; set;}
|
||||
public string keypad7Tag {get; set;}
|
||||
public string keypad8Tag {get; set;}
|
||||
public string keypad9Tag {get; set;}
|
||||
public string keypad0Tag {get; set;}
|
||||
public string keypadPoundTag {get; set;}
|
||||
public string keypadStarTag {get; set;}
|
||||
|
||||
public bool ClearOnHangup { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class QscDspPresets
|
||||
{
|
||||
public string label { get; set; }
|
||||
public string preset { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user