IPCClient.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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. OnPush?.Invoke(e.Message);
  89. }
  90. }
  91. private void Client_Connected(object? sender, H.Pipes.Args.ConnectionEventArgs<PipeRequest> e)
  92. {
  93. Logger.Send(LogType.Information, "", $"Connected to Pipe: {e.Connection.PipeName}");
  94. Disconnected = false;
  95. OnConnect?.Invoke();
  96. }
  97. private void Client_Disconnected(object? sender, H.Pipes.Args.ConnectionEventArgs<PipeRequest> e)
  98. {
  99. Logger.Send(LogType.Information, "", $"Disconnected from Pipe: {e.Connection.PipeName}");
  100. foreach (var ev in Events)
  101. {
  102. Responses.TryAdd(ev.Key, PipeRequest.Error(RequestError.DISCONNECTED));
  103. ev.Value.Set();
  104. }
  105. Disconnected = true;
  106. OnDisconnect?.Invoke();
  107. }
  108. public void Dispose()
  109. {
  110. Client.DisposeAsync().AsTask().Wait();
  111. }
  112. ~IPCClient()
  113. {
  114. Dispose();
  115. }
  116. }
  117. }