using Google.Protobuf; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using Tango.PMR; using Tango.PMR.Common; using Tango.Stubs; using Tango.Transport; using Tango.Transport.Adapters; namespace Tango.MachineStudio.Stubs { /// /// Represents a manager capable of executing stub scripts asynchronously. /// public class StubManager { public ITransportAdapter Adapter { get; private set; } /// /// Occurs when the stub has failed to execute. /// public event EventHandler Failed; /// /// Occurs when the stub has completed successfully. /// public event EventHandler Completed; /// /// Occurs when the stub has been initialized and executed. /// public event EventHandler Executed; /// /// Gets a value indicating whether this is aborted. /// internal bool Aborted { get; private set; } /// /// Initializes a new instance of the class. /// /// The adapter. public StubManager(ITransportAdapter adapter) { Adapter = adapter; } /// /// Aborts the current script. /// internal void Abort() { Aborted = true; } /// /// Runs the specified stub name. /// /// Name of the stub. /// The arguments. public void Run(String stubName, params Object[] args) { if (Aborted) return; var stubType = StubBase.GetAvailableRequestStubs().SingleOrDefault(x => x.Name.ToLower() == stubName.ToLower() || x.Name.Replace("Request", "").ToLower() == stubName.ToLower()); if (stubType == null) { OnFailed(new ArgumentException("Invalid stub '" + stubName + "'.")); return; } var stubProps = stubType.GetProperties(BindingFlags.Public | BindingFlags.Instance); if (stubProps.Length > args.Length) { OnFailed(new ArgumentOutOfRangeException("Not enough arguments for " + stubType.Name + ".")); return; } Executed?.Invoke(this, stubType.Name); try { MessageContainer container = new MessageContainer(); container.Token = Guid.NewGuid().ToString(); container.Type = MessageFactory.ParseMessageType(stubType.Name); Object request = Activator.CreateInstance(stubType); int argIndex = 0; foreach (var prop in stubProps) { String arg = args[argIndex++].ToString(); if (prop.PropertyType == typeof(UInt32)) { prop.SetValue(request, UInt32.Parse(arg)); } else if (prop.PropertyType == typeof(bool)) { prop.SetValue(request, bool.Parse(arg)); } else { object converted = Convert.ChangeType(arg, prop.PropertyType); prop.SetValue(request, converted); } } container.Data = typeof(IMessage).GetExtensionMethod(typeof(ByteString).Assembly, "ToByteString").Invoke(request, new object[] { request }) as ByteString; byte[] requestData = container.ToByteArray(); bool done = false; Task.Factory.StartNew(() => { Adapter.Write(requestData); DateTime startTime = DateTime.Now; MessageContainer responseContainer = null; Adapter.DataAvailable += (sender, data) => { responseContainer = MessageFactory.ParseContainer(data); }; while (responseContainer == null) { Thread.Sleep(2); if (DateTime.Now > startTime.AddSeconds(2)) { done = true; OnFailed(new TimeoutException("Response has failed to arrive after 2 seconds.")); return; } } IMessage message = MessageFactory.ExtractMessageFromContainer(responseContainer); OnCompleted(JsonConvert.SerializeObject(message, Formatting.Indented)); done = true; }); while (!done) { Thread.Sleep(2); } } catch (Exception ex) { OnFailed(ex); } } /// /// Raises the event. /// /// The exception. protected virtual void OnFailed(Exception ex) { Failed?.Invoke(this, ex); } /// /// Raises the event. /// /// The response. protected virtual void OnCompleted(String response) { Completed?.Invoke(this, response); } } }