X-Git-Url: https://git.novaco.in/?p=StratumLibrary.git;a=blobdiff_plain;f=StratumLibrary%2FStratum.cs;h=d023f6a37e20e12e5c24e7482ceb855cf910e7cf;hp=56520edda14c79976c7f5e964d40b3c6566b2660;hb=5ac1ea8b2c1c4eca105471979abafa6c637b67a9;hpb=3873f3ffa728da77ccd2429ecdc527ee408ea40f diff --git a/StratumLibrary/Stratum.cs b/StratumLibrary/Stratum.cs index 56520ed..d023f6a 100644 --- a/StratumLibrary/Stratum.cs +++ b/StratumLibrary/Stratum.cs @@ -1,10 +1,12 @@ using System; using System.Net; +using System.Threading; +using System.Net.Sockets; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Text; -using System.Threading; -using System.Net.Sockets; +using System.Collections.Generic; namespace Stratum { @@ -12,6 +14,10 @@ namespace Stratum { private Socket client; + object responsesLock = new object(); + private Dictionary responses = new Dictionary(); + ManualResetEvent gotResponse = new ManualResetEvent(false); + /// /// Constructor of Stratum interface class /// @@ -32,10 +38,10 @@ namespace Stratum ConnectCallback = (IAsyncResult ar) => { // Retrieve socket from the state object - Socket cli = (Socket)ar.AsyncState; + Socket arClient = (Socket)ar.AsyncState; // Complete the connection - cli.EndConnect(ar); + arClient.EndConnect(ar); // Signal that connection has been established connectDone.Set(); @@ -46,6 +52,9 @@ namespace Stratum // Wait for signal connectDone.WaitOne(); + + // Start receive handler + Receiver(client); } ~Stratum() @@ -64,10 +73,10 @@ namespace Stratum SendCallback = (IAsyncResult ar) => { // Retrieve the socket from the state object - Socket cli = (Socket)ar.AsyncState; + Socket arClient = (Socket)ar.AsyncState; // Complete sending the data to the remote device - int bytesSent = cli.EndSend(ar); + int bytesSent = arClient.EndSend(ar); // Signal that all bytes have been sent sendDone.Set(); @@ -88,6 +97,22 @@ namespace Stratum /// /// Return type /// Method name + /// StratumResponse object + public StratumResponse Invoke(string method) + { + var req = new StratumRequest() + { + Method = method, + Params = new object[] { } + }; + return Invoke(req); + } + + /// + /// Invoke remote method + /// + /// Return type + /// Method name /// Argument /// StratumResponse object public StratumResponse Invoke(string method, object arg) @@ -119,75 +144,118 @@ namespace Stratum private StratumResponse Invoke(StratumRequest stratumReq) { - StratumResponse rjson = null; - // Serialize stratumReq into JSON string - var reqJSON = Newtonsoft.Json.JsonConvert.SerializeObject(stratumReq) + '\n'; + var reqJSON = JsonConvert.SerializeObject(stratumReq) + '\n'; // Send JSON data to the remote device. Send(client, reqJSON); + // Wait for response + gotResponse.WaitOne(); + + var strResponse = string.Empty; + lock (responsesLock) + { + // Deserialize the response + strResponse = responses[stratumReq.Id]; + responses.Remove(stratumReq.Id); + } + + // Deserialize response into new instance of StratumResponse + StratumResponse responseObj = JsonConvert.DeserializeObject>(strResponse); + + // Reset the state + gotResponse.Reset(); + + if (responseObj == null) + { + try + { + JObject jResponseObj = JsonConvert.DeserializeObject(strResponse) as JObject; + throw new Exception(jResponseObj["Error"].ToString()); + } + catch (JsonSerializationException) + { + throw new Exception("Inconsistent or empty response"); + } + } + + return responseObj; + } + + private void Receiver(Socket client) + { // Create the reading state object. StratumReadState state = new StratumReadState(client); - // Receive event - ManualResetEvent receiveDone = new ManualResetEvent(false); - Action ReceiveCallback = null; ReceiveCallback = (IAsyncResult ar) => { // Retrieve the state object and the client socket // from the asynchronous state object. - StratumReadState st = (StratumReadState)ar.AsyncState; - Socket ci = st.workSocket; + StratumReadState arStatus = (StratumReadState)ar.AsyncState; + Socket arClient = arStatus.workSocket; // Read data from the remote device. - int bytesRead = ci.EndReceive(ar); + int bytesRead = arClient.EndReceive(ar); if (bytesRead <= 0) return; - lock (st.sb) + lock (arStatus.sb) { // There might be more data, so store the data received so far. - st.sb.Append(Encoding.ASCII.GetString(st.buffer, 0, bytesRead)); + arStatus.sb.Append(Encoding.ASCII.GetString(arStatus.buffer, 0, bytesRead)); - if (st.buffer[bytesRead - 1] != '\n') + if (arStatus.buffer[bytesRead - 1] == '\n') { - // No EOL at the end of buffer, going to get the rest of data - ci.BeginReceive(st.buffer, 0, StratumReadState.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), st); - } - else - { - string strResponse = st.sb.ToString(); - - // Deserialize the response - rjson = Newtonsoft.Json.JsonConvert.DeserializeObject>(strResponse); + var strMessage = arStatus.sb.ToString(); + arStatus.sb.Clear(); - if (rjson == null) + try { - try + JObject jResponse = JsonConvert.DeserializeObject(strMessage) as JObject; + var reqId = (string)jResponse["id"]; + + if (!String.IsNullOrEmpty(reqId)) { - JObject jo = Newtonsoft.Json.JsonConvert.DeserializeObject(strResponse) as JObject; - throw new Exception(jo["Error"].ToString()); + lock (responsesLock) + { + responses.Add(reqId, strMessage); + } + + gotResponse.Set(); } - catch (Newtonsoft.Json.JsonSerializationException) + else { - throw new Exception("Inconsistent or empty response"); + StratumNotification jNotification = JsonConvert.DeserializeObject(strMessage); + + var NotifyProcessThread = new Thread(() => NotificationHandler(jNotification.Method, jNotification.Params)); + NotifyProcessThread.Start(); } } - - // Signal that all bytes have been received. - receiveDone.Set(); + catch (JsonSerializationException e) + { + // TODO: handle parse error + } } } + + arClient.BeginReceive(arStatus.buffer, 0, StratumReadState.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), arStatus); }; - // Begin receiving the data from the remote device. client.BeginReceive(state.buffer, 0, StratumReadState.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), state); - receiveDone.WaitOne(); + } - return rjson; + /// + /// Notifications stub which is run in a separate thread. If you wish to implement real notification processing then just override this method in the derived class. + /// + /// Method name + /// Array of values + private static void NotificationHandler(string NotificationMethod, JArray NotificationData) + { + Console.WriteLine("\nNotification: Method={0}, data={1}", NotificationMethod, NotificationData.ToString()); } } + } \ No newline at end of file