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);
}
}
}