IPCClient.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. using H.Pipes;
  2. using InABox.Core;
  3. using InABox.IPC.Shared;
  4. using System;
  5. using System.Collections.Concurrent;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. namespace InABox.Client.IPC
  11. {
  12. public class IPCClient : IDisposable
  13. {
  14. private PipeClient<PipeRequest> Client;
  15. private ConcurrentDictionary<Guid, ManualResetEventSlim> Events = new();
  16. private ConcurrentDictionary<Guid, PipeRequest> Responses = new();
  17. private const int DefaultRequestTimeout = 5 * 60 * 1000; // 5 minutes
  18. public delegate void ConnectEvent();
  19. public delegate void DisconnectEvent();
  20. /// <summary>
  21. /// A handler for any requests pushed from the server, i.e., not initialised by the client.
  22. /// </summary>
  23. public delegate void PushEvent(PipeRequest request);
  24. public bool Disconnected { get; private set; }
  25. public event ConnectEvent? OnConnect;
  26. public event DisconnectEvent? OnDisconnect;
  27. public event PushEvent? OnPush;
  28. public IPCClient(string name)
  29. {
  30. Client = new PipeClient<PipeRequest>(name);
  31. Client.Connected += Client_Connected;
  32. Client.Disconnected += Client_Disconnected;
  33. Client.MessageReceived += Client_MessageReceived;
  34. Client.ExceptionOccurred += Client_ExceptionOccurred;
  35. Client.ConnectAsync();
  36. }
  37. private void Client_ExceptionOccurred(object? sender, H.Pipes.Args.ExceptionEventArgs e)
  38. {
  39. Logger.Send(LogType.Error, "", $"Exception occured: {e.Exception.Message}");
  40. }
  41. public PipeRequest Send(PipeRequest request, int timeout = DefaultRequestTimeout)
  42. {
  43. var start = DateTime.Now;
  44. var ev = Queue(request.RequestID);
  45. Client.WriteAsync(request);
  46. var result = GetResult(request.RequestID, ev, timeout);
  47. return result;
  48. }
  49. public ManualResetEventSlim Queue(Guid id)
  50. {
  51. var ev = new ManualResetEventSlim();
  52. Events[id] = ev;
  53. return ev;
  54. }
  55. public PipeRequest GetResult(Guid id, ManualResetEventSlim ev, int timeout)
  56. {
  57. if (Responses.TryGetValue(id, out var result))
  58. {
  59. Responses.Remove(id, out result);
  60. Events.Remove(id, out ev);
  61. return result;
  62. }
  63. try
  64. {
  65. if (!ev.Wait(timeout))
  66. {
  67. return PipeRequest.Error(RequestError.TIMEOUT);
  68. }
  69. }
  70. catch (Exception e)
  71. {
  72. Console.WriteLine(e);
  73. throw;
  74. }
  75. Responses.Remove(id, out result);
  76. Events.Remove(id, out ev);
  77. return result ?? PipeRequest.Error(RequestError.UNKNOWN);
  78. }
  79. private void Client_MessageReceived(object? sender, H.Pipes.Args.ConnectionMessageEventArgs<PipeRequest?> e)
  80. {
  81. if (Events.TryGetValue(e.Message.RequestID, out var ev))
  82. {
  83. Responses[e.Message.RequestID] = e.Message;
  84. ev.Set();
  85. }
  86. else
  87. {
  88. Task.Run(() =>
  89. {
  90. OnPush?.Invoke(e.Message);
  91. }).ContinueWith(task =>
  92. {
  93. if (task.Exception != null)
  94. {
  95. Logger.Send(LogType.Error, "", $"Error in IPC Client Push: {CoreUtils.FormatException(task.Exception)}");
  96. }
  97. });
  98. }
  99. }
  100. private void Client_Connected(object? sender, H.Pipes.Args.ConnectionEventArgs<PipeRequest> e)
  101. {
  102. Logger.Send(LogType.Information, "", $"Connected to Pipe: {e.Connection.PipeName}");
  103. Disconnected = false;
  104. OnConnect?.Invoke();
  105. }
  106. private void Client_Disconnected(object? sender, H.Pipes.Args.ConnectionEventArgs<PipeRequest> e)
  107. {
  108. Logger.Send(LogType.Information, "", $"Disconnected from Pipe: {e.Connection.PipeName}");
  109. foreach (var ev in Events)
  110. {
  111. Responses.TryAdd(ev.Key, PipeRequest.Error(RequestError.DISCONNECTED));
  112. ev.Value.Set();
  113. }
  114. Disconnected = true;
  115. OnDisconnect?.Invoke();
  116. }
  117. public void Dispose()
  118. {
  119. Client.DisposeAsync().AsTask().Wait();
  120. }
  121. ~IPCClient()
  122. {
  123. Dispose();
  124. }
  125. }
  126. }