From a9524bcc33a5494e92db07c666159cd8c9825f1f Mon Sep 17 00:00:00 2001 From: Trevor Payne Date: Wed, 21 Oct 2020 16:30:48 -0500 Subject: [PATCH] Updated Interface to be more genericized --- .../Devices/IDspPreset.cs | 16 +- .../DSP/BiampTesira/BiampTesiraForteDsp.cs | 807 +++++++++--------- .../Essentials Devices Common/DSP/DspBase.cs | 26 +- 3 files changed, 448 insertions(+), 401 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IDspPreset.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IDspPreset.cs index c565e0d9..2ec63fcb 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IDspPreset.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/IDspPreset.cs @@ -1,9 +1,17 @@ -namespace PepperDash.Essentials.Core +using System.Collections.Generic; + +namespace PepperDash.Essentials.Core { + public interface IHasDspPresets + { + List Presets { get; } + + void RecallPreset(IDspPreset preset); + + } + public interface IDspPreset { - void RunPreset(string name); - - void RunPreset(int id); + string Name { get; } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraForteDsp.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraForteDsp.cs index 9e397f19..9cce66f0 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraForteDsp.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/BiampTesira/BiampTesiraForteDsp.cs @@ -6,395 +6,426 @@ using Crestron.SimplSharp; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; -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 LevelControlPoints { get; private set; } - - public bool isSubscribed; - - private CTimer SubscriptionTimer; - - CrestronQueue CommandQueue; - - bool CommandQueueInProgress = false; - - //new public Dictionary DialerControlPoints { get; private set; } - - //new public Dictionary SwitcherControlPoints { get; private set; } - - /// - /// Shows received lines as hex - /// - 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(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(); - - foreach (KeyValuePair 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; - } - } - - /// - /// Initiates the subscription process to the DSP - /// - void SubscribeToAttributes() - { - SendLine("SESSION set verbose true"); - - foreach (KeyValuePair level in LevelControlPoints) - { - level.Value.Subscribe(); - } - - if (!CommandQueueInProgress) - SendNextQueuedCommand(); - - ResetSubscriptionTimer(); - } - - /// - /// Resets or Sets the subscription timer - /// - void ResetSubscriptionTimer() - { - isSubscribed = true; - - if (SubscriptionTimer != null) - { - SubscriptionTimer = new CTimer(o => SubscribeToAttributes(), 30000); - SubscriptionTimer.Reset(); - - } - } - - /// - /// Handles a response message from the DSP - /// - /// - /// - 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 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); - } - - } - - /// - /// Sends a command to the DSP (with delimiter appended) - /// - /// Command to send - public void SendLine(string s) - { - Debug.Console(1, this, "TX: '{0}'", s); - Communication.SendText(s + "\x0a"); - } - - /// - /// Adds a command from a child module to the queue - /// - /// Command object from child module - 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(); - } +using System.Text.RegularExpressions; - public override void RunPreset(int data) + +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; } + + public new Dictionary LevelControlPoints { get; private set; } + + public bool isSubscribed; + + private CTimer SubscriptionTimer; + + private CrestronQueue CommandQueue; + + private bool CommandQueueInProgress = false; + + //new public Dictionary DialerControlPoints { get; private set; } + + //new public Dictionary SwitcherControlPoints { get; private set; } + + /// + /// Shows received lines as hex + /// + public bool ShowHexResponse { get; set; } + + public BiampTesiraForteDsp(string key, string name, IBasicCommunication comm, + BiampTesiraFortePropertiesConfig props) : + base(key, name) { - SendLine(String.Format("Device recallPreset {0}", data )); + CommandQueue = new CrestronQueue(100); + + Communication = comm; + var socket = comm as ISocketStatus; + if (socket != null) + { + // This instance uses IP control + + socket.ConnectionChange += new EventHandler(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(); + + foreach (KeyValuePair block in props.LevelControlBlocks) + { + this.LevelControlPoints.Add(block.Key, new TesiraForteLevelControl(block.Key, block.Value, this)); + } + } - /// - /// Adds a raw string command to the queue - /// - /// - 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(); - } - - /// - /// Sends the next queued command to the DSP - /// - 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); - } - } - - } - - /// - /// Sends a command to execute a preset - /// - /// Preset Name - 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; } - } - } - - public class BiampTesiraForteDspFactory : EssentialsDeviceFactory - { - public BiampTesiraForteDspFactory() - { - TypeNames = new List() { "biamptesira" }; - } - - public override EssentialsDevice BuildDevice(DeviceConfig dc) - { - Debug.Console(1, "Factory Attempting to create new BiampTesira Device"); - var comm = CommFactory.CreateCommForDevice(dc); - var props = Newtonsoft.Json.JsonConvert.DeserializeObject( - dc.Properties.ToString()); - return new BiampTesiraForteDsp(dc.Key, dc.Name, comm, props); - } - } - -} \ No newline at end of file + 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; + } + + private 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; + } + } + + /// + /// Initiates the subscription process to the DSP + /// + private void SubscribeToAttributes() + { + SendLine("SESSION set verbose true"); + + foreach (KeyValuePair level in LevelControlPoints) + { + level.Value.Subscribe(); + } + + if (!CommandQueueInProgress) + SendNextQueuedCommand(); + + ResetSubscriptionTimer(); + } + + /// + /// Resets or Sets the subscription timer + /// + private void ResetSubscriptionTimer() + { + isSubscribed = true; + + if (SubscriptionTimer != null) + { + SubscriptionTimer = new CTimer(o => SubscribeToAttributes(), 30000); + SubscriptionTimer.Reset(); + + } + } + + /// + /// Handles a response message from the DSP + /// + /// + /// + private 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 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); + } + + } + + /// + /// Sends a command to the DSP (with delimiter appended) + /// + /// Command to send + public void SendLine(string s) + { + Debug.Console(1, this, "TX: '{0}'", s); + Communication.SendText(s + "\x0a"); + } + + /// + /// Adds a command from a child module to the queue + /// + /// Command object from child module + 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(); + } + + public void RecallPreset(IDspPreset preset) + { + if (preset == null) return; + + var tesiraPreset = preset as TesiraDspPreset; + + if (tesiraPreset == null) return; + if (!String.IsNullOrEmpty(tesiraPreset.PresetName)) + { + SendLine(String.Format("Device RecallPreset {0}", tesiraPreset.PresetName)); + } + if (tesiraPreset.PresetId >= 1000) + { + SendLine(String.Format("Device RecallPreset {0}", tesiraPreset.PresetId)); + } + else + { + Debug.Console(0, this, "Preset {0} unable to be recalled, missing identifier", tesiraPreset.Name); + } + } + + /// + /// Adds a raw string command to the queue + /// + /// + 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(); + } + + /// + /// Sends the next queued command to the DSP + /// + private 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); + } + } + + } + + /// + /// Sends a command to execute a preset + /// + /// Preset Name + public 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; } + } + } + + public class BiampTesiraForteDspFactory : EssentialsDeviceFactory + { + public BiampTesiraForteDspFactory() + { + TypeNames = new List() {"biamptesira"}; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new BiampTesira Device"); + var comm = CommFactory.CreateCommForDevice(dc); + var props = Newtonsoft.Json.JsonConvert.DeserializeObject( + dc.Properties.ToString()); + return new BiampTesiraForteDsp(dc.Key, dc.Name, comm, props); + } + } + + public class TesiraDspPreset : IDspPreset + { + public string Name { get; set; } + public readonly string PresetName; + public readonly int PresetId; + + } + +} diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/DspBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/DspBase.cs index 62043827..e8e38598 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/DspBase.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/DSP/DspBase.cs @@ -9,26 +9,34 @@ using PepperDash.Essentials.Core; namespace PepperDash.Essentials.Devices.Common.DSP { - public abstract class DspBase : EssentialsDevice, IDspPreset + public abstract class DspBase : EssentialsDevice, IHasDspPresets { public Dictionary LevelControlPoints { get; private set; } public Dictionary DialerControlPoints { get; private set; } - public Dictionary SwitcherControlPoints { get; private set; } - - public abstract void RunPreset(string name); + public Dictionary SwitcherControlPoints { get; private set; } - public abstract void RunPreset(int data); - - public DspBase(string key, string name) : - base(key, name) { } + public DspBase(string key, string name) : + base(key, name) + { + Presets = new List(); + } // in audio call feedback // VOIP - // Phone dialer + // Phone dialer + + + public List Presets { get; set; } + + + public void RecallPreset(IDspPreset preset) + { + } + } // Fusion