From 32c89fd6aae59ad612b903e130c88753ff09d9fb Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 5 Dec 2023 15:32:44 -0700 Subject: [PATCH] feat: updates to allow telnet communication and handle login sequence. Updates for reliability and performance --- .gitignore | 1 + src/Abstractions/IApDeviceBuilder.cs | 2 + src/Builders/Ap89XxBuilder.cs | 3 + src/Devices/ApDevice.cs | 132 +++++++++++++++++++++------ src/Factories/Ap89XxFactory.cs | 2 +- 5 files changed, 110 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 542e6bb..f932e5a 100644 --- a/.gitignore +++ b/.gitignore @@ -363,3 +363,4 @@ MigrationBackup/ *.projectinfo SPlsWork/ +src/Properties/ApcEpi.cplz diff --git a/src/Abstractions/IApDeviceBuilder.cs b/src/Abstractions/IApDeviceBuilder.cs index 388fe54..143b937 100644 --- a/src/Abstractions/IApDeviceBuilder.cs +++ b/src/Abstractions/IApDeviceBuilder.cs @@ -7,6 +7,7 @@ using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Queues; using PepperDash_Essentials_Core.Devices; +using ApcEpi.Config; namespace ApcEpi.Abstractions @@ -18,5 +19,6 @@ public interface IApDeviceBuilder : IKeyName EssentialsDevice Build(); bool UseEssentialsJoinMap { get; } bool EnableAsOnline { get; } + ApDeviceConfig Config { get; } } } \ No newline at end of file diff --git a/src/Builders/Ap89XxBuilder.cs b/src/Builders/Ap89XxBuilder.cs index ce34974..6f29bd8 100644 --- a/src/Builders/Ap89XxBuilder.cs +++ b/src/Builders/Ap89XxBuilder.cs @@ -13,11 +13,14 @@ namespace ApcEpi.Builders { public class Ap89XxBuilder : IApDeviceBuilder { + public ApDeviceConfig Config { get; private set; } + private Ap89XxBuilder(string key, string name, IBasicCommunication coms, ApDeviceConfig config) { Coms = coms; Name = name; Key = key; + Config = config; UseEssentialsJoinMap = config.UseEssentialsJoinmap; Outlets = BuildOutletsFromConfig(key, config, coms); diff --git a/src/Devices/ApDevice.cs b/src/Devices/ApDevice.cs index f363c8b..4c18e75 100644 --- a/src/Devices/ApDevice.cs +++ b/src/Devices/ApDevice.cs @@ -12,12 +12,23 @@ using PepperDash.Essentials.Core.Bridges; using Feedback = PepperDash.Essentials.Core.Feedback; using PepperDash_Essentials_Core.Devices; +using ApcEpi.Config; namespace ApcEpi.Devices { public class ApDevice : EssentialsBridgeableDevice, IOutletName, IOutletPower, IOutletOnline, IHasControlledPowerOutlets { + private ApDeviceConfig _config; + + private readonly IBasicCommunication _comms; + private readonly string _password = "apc"; // sets default + private readonly string _username = "apc"; // sets default + private const string DELIMITER = "\r"; + + // Used to track the login state of telnet connections + private bool _loggedIn = false; + private readonly StatusMonitorBase _monitor; public ReadOnlyDictionary PduOutlets { get; private set; } bool _useEssentialsJoinmap; @@ -26,16 +37,45 @@ public class ApDevice : EssentialsBridgeableDevice, IOutletName, IOutletPower, I public ApDevice(IApDeviceBuilder builder) : base(builder.Key, builder.Name) { + _comms = builder.Coms; + _config = builder.Config; + + if (!string.IsNullOrEmpty(_config.Control.TcpSshProperties.Username)) + { + _username = _config.Control.TcpSshProperties.Username; + } if (!string.IsNullOrEmpty(_config.Control.TcpSshProperties.Password)) + { + _password = _config.Control.TcpSshProperties.Password; + } + Feedbacks = new FeedbackCollection(); PduOutlets = builder.Outlets; + //_monitor = new GenericCommunicationMonitor( + // this, + // builder.Coms, + // 30000, + // 120000, + // 240000, + // ApOutletStatusCommands.GetAllOutletStatusCommand()); _monitor = new GenericCommunicationMonitor( - this, - builder.Coms, - 30000, - 120000, + this, + builder.Coms, + 30000, + 120000, 240000, - ApOutletStatusCommands.GetAllOutletStatusCommand()); + () => + { + //Debug.Console(2, this, "Polling for status"); + + //Debug.Console(2, this, "Block Poll?: {0}", _config.Control.Method == eControlMethod.Tcpip && !_loggedIn); + + if (_config.Control.Method == eControlMethod.Tcpip && !_loggedIn) return; + + + SendText(ApOutletStatusCommands.GetAllOutletStatusCommand()); + }); + NameFeedback = new StringFeedback("DeviceNameFeedback", () => Name); Feedbacks.Add(IsOnline); @@ -43,19 +83,32 @@ public ApDevice(IApDeviceBuilder builder) EnableAsOnline = builder.EnableAsOnline; _useEssentialsJoinmap = builder.UseEssentialsJoinMap; + _comms.TextReceived += new EventHandler(_comms_TextReceived); + + //var loginPromptGather = new CommunicationGather(_comms, DELIMITER); + //loginPromptGather.LineReceived += _comms_TextReceived; + var gather = new CommunicationGather(builder.Coms, "\n"); gather.LineReceived += - (o, textArgs) => ProcessResponse(PduOutlets, textArgs.Text); - - /* + (o, textArgs) => + { + if (_config.Control.Method == eControlMethod.Tcpip && !_loggedIn) return; + ProcessResponse(PduOutlets, textArgs.Text); + }; + var socket = builder.Coms as ISocketStatus; if (socket != null) { socket.ConnectionChange += (sender, args) => - Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "ConnectionStatus:{0}", - args.Client.ClientStatus.ToString()); - }*/ + { + + if (!socket.IsConnected) + { + _loggedIn = false; + } + }; + } CrestronEnvironment.ProgramStatusEventHandler += type => { @@ -64,33 +117,54 @@ public ApDevice(IApDeviceBuilder builder) CommunicationMonitor.Stop(); }; + } - DeviceManager.AllDevicesActivated += (sender, args) => - { - try - { - builder.Coms.Connect(); - CommunicationMonitor.Start(); - } - catch (Exception ex) - { - Debug.Console(0, - this, - Debug.ErrorLogLevel.Error, - "Error handling all devices activated : {0}", - ex.Message); - } - }; + public override void Initialize() + { + _comms.Connect(); + + if (_config.Control.Method == eControlMethod.Ssh) + CommunicationMonitor.Start(); } - public static void ProcessResponse(ReadOnlyDictionary outlets, string response) + void _comms_TextReceived(object sender, GenericCommMethodReceiveTextArgs e) { + if (e.Text.Contains("User Name")) + { + Debug.Console(2, this, "Attempting to Log in..."); + SendText(_username); + return; + } + else if (e.Text.Contains("Password")) + { + SendText(_password); + return; + } + else if (e.Text.Contains("apc>")) + { + if (!_loggedIn) + Debug.Console(2, this, "Logged in Successfully."); + + _loggedIn = true; + CommunicationMonitor.Start(); + } + } + + public void SendText(string text) + { + _comms.SendText(text + DELIMITER); + } + + public void ProcessResponse(ReadOnlyDictionary outlets, string response) + { + var responseToProcess = response.Trim().Split(new[] { ':' }); if (responseToProcess.Count() != 3) return; try { + var outletIndex = Convert.ToUInt32(responseToProcess[0]); IHasPowerCycle outlet; if (!outlets.TryGetValue((int)outletIndex, out outlet)) @@ -116,7 +190,7 @@ public static void ProcessResponse(ReadOnlyDictionary outle } catch (Exception ex) { - Debug.Console(1, "Error processing response: {0}{1}", response, ex.Message); + Debug.Console(1, this, "Error processing response: {0}{1}", response, ex.Message); } } diff --git a/src/Factories/Ap89XxFactory.cs b/src/Factories/Ap89XxFactory.cs index ed38ccd..8e8f60a 100644 --- a/src/Factories/Ap89XxFactory.cs +++ b/src/Factories/Ap89XxFactory.cs @@ -9,7 +9,7 @@ public class Ap89XxFactory : EssentialsPluginDeviceFactory { public Ap89XxFactory() { - MinimumEssentialsFrameworkVersion = "1.11.0"; + MinimumEssentialsFrameworkVersion = "1.15.0"; TypeNames = new List() { "Ap89xx" }; }