722abdacc2dae2f1070551c8ec0235f495917790
[StratumLibrary.git] / StratumLibrary / Stratum.cs
1 \feffusing System;\r
2 using System.Net;\r
3 using System.Threading;\r
4 using System.Net.Sockets;\r
5 using Newtonsoft.Json.Linq;\r
6 \r
7 using System.Text;\r
8 using System.Collections.Generic;\r
9 \r
10 namespace Stratum\r
11 {\r
12     public class Stratum\r
13     {\r
14         private Socket client;\r
15         private Dictionary<string, string> responses = new Dictionary<string, string>();\r
16         ManualResetEvent gotResponse = new ManualResetEvent(false);\r
17 \r
18         /// <summary>\r
19         /// Constructor of Stratum interface class\r
20         /// </summary>\r
21         /// <param name="ipAddress">IPv4 address</param>\r
22         /// <param name="port">Port number</param>\r
23         public Stratum(string ipAddress, int port)\r
24         {\r
25             // End point for the remote device\r
26             IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ipAddress), port);\r
27 \r
28             // Create TCP socket\r
29             client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);\r
30 \r
31             // Connect done event\r
32             ManualResetEvent connectDone = new ManualResetEvent(false);\r
33 \r
34             Action<IAsyncResult> ConnectCallback = null;\r
35             ConnectCallback = (IAsyncResult ar) =>\r
36             {\r
37                 // Retrieve socket from the state object\r
38                 Socket arClient = (Socket)ar.AsyncState;\r
39 \r
40                 // Complete the connection\r
41                 arClient.EndConnect(ar);\r
42 \r
43                 // Signal that connection has been established\r
44                 connectDone.Set();\r
45             };\r
46 \r
47             // Connect to the remote device\r
48             client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);\r
49 \r
50             // Wait for signal\r
51             connectDone.WaitOne();\r
52 \r
53             // Start receive handler\r
54             Receiver(client);\r
55         }\r
56 \r
57         ~Stratum()\r
58         {\r
59             // Release the socket\r
60             client.Shutdown(SocketShutdown.Both);\r
61             client.Close();\r
62         }\r
63 \r
64         private void Send(Socket client, String data)\r
65         {\r
66             // Send done event\r
67             ManualResetEvent sendDone = new ManualResetEvent(false);\r
68 \r
69             Action<IAsyncResult> SendCallback = null;\r
70             SendCallback = (IAsyncResult ar) =>\r
71             {\r
72                 // Retrieve the socket from the state object\r
73                 Socket arClient = (Socket)ar.AsyncState;\r
74 \r
75                 // Complete sending the data to the remote device\r
76                 int bytesSent = arClient.EndSend(ar);\r
77 \r
78                 // Signal that all bytes have been sent\r
79                 sendDone.Set();\r
80             };\r
81 \r
82             // Convert the string data to byte data using ASCII encoding.\r
83             byte[] byteData = Encoding.ASCII.GetBytes(data);\r
84 \r
85             // Begin sending the data to the remote device.\r
86             client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);\r
87 \r
88             // Wait for signal\r
89             sendDone.WaitOne();\r
90         }\r
91 \r
92         /// <summary>\r
93         /// Invoke remote method\r
94         /// </summary>\r
95         /// <typeparam name="T">Return type</typeparam>\r
96         /// <param name="method">Method name</param>\r
97         /// <param name="arg">Argument</param>\r
98         /// <returns>StratumResponse object</returns>\r
99         public StratumResponse<T> Invoke<T>(string method, object arg)\r
100         {\r
101             var req = new StratumRequest()\r
102             {\r
103                 Method = method,\r
104                 Params = new object[] { arg }\r
105             };\r
106             return Invoke<T>(req);\r
107         }\r
108 \r
109         /// <summary>\r
110         /// Invoke remote method\r
111         /// </summary>\r
112         /// <typeparam name="T">Return type</typeparam>\r
113         /// <param name="method">Method name</param>\r
114         /// <param name="args">Arguments array</param>\r
115         /// <returns>StratumResponse object</returns>\r
116         public StratumResponse<T> Invoke<T>(string method, object[] args)\r
117         {\r
118             var req = new StratumRequest()\r
119             {\r
120                 Method = method,\r
121                 Params = args\r
122             };\r
123             return Invoke<T>(req);\r
124         }\r
125 \r
126         private StratumResponse<T> Invoke<T>(StratumRequest stratumReq)\r
127         {\r
128             // Serialize stratumReq into JSON string\r
129             var reqJSON = Newtonsoft.Json.JsonConvert.SerializeObject(stratumReq) + '\n';\r
130             var reqId = (string) stratumReq.Id;\r
131 \r
132             // Send JSON data to the remote device.\r
133             Send(client, reqJSON);\r
134 \r
135             // Wait for response\r
136             gotResponse.WaitOne();\r
137 \r
138             // Deserialize the response\r
139             string strResponse = responses[reqId];\r
140             StratumResponse<T> responseObj = Newtonsoft.Json.JsonConvert.DeserializeObject<StratumResponse<T>>(strResponse);\r
141             responses.Remove(reqId);\r
142 \r
143             // Reset the state\r
144             gotResponse.Reset();\r
145 \r
146             if (responseObj == null)\r
147             {\r
148                 try\r
149                 {\r
150                     JObject jo = Newtonsoft.Json.JsonConvert.DeserializeObject(strResponse) as JObject;\r
151                     throw new Exception(jo["Error"].ToString());\r
152                 }\r
153                 catch (Newtonsoft.Json.JsonSerializationException)\r
154                 {\r
155                     throw new Exception("Inconsistent or empty response");\r
156                 }\r
157             }\r
158 \r
159             return responseObj;\r
160         }\r
161 \r
162         private void Receiver(Socket client)\r
163         {\r
164             // Create the reading state object.\r
165             StratumReadState state = new StratumReadState(client);\r
166 \r
167             Action<IAsyncResult> ReceiveCallback = null;\r
168             ReceiveCallback = (IAsyncResult ar) =>\r
169             {\r
170                 // Retrieve the state object and the client socket \r
171                 // from the asynchronous state object.\r
172                 StratumReadState arStatus = (StratumReadState)ar.AsyncState;\r
173                 Socket arClient = arStatus.workSocket;\r
174 \r
175                 // Read data from the remote device.\r
176                 int bytesRead = arClient.EndReceive(ar);\r
177 \r
178                 if (bytesRead <= 0)\r
179                     return;\r
180 \r
181                 lock (arStatus.sb)\r
182                 {\r
183                     // There might be more data, so store the data received so far.\r
184                     arStatus.sb.Append(Encoding.ASCII.GetString(arStatus.buffer, 0, bytesRead));\r
185 \r
186                     if (arStatus.buffer[bytesRead - 1] == '\n')\r
187                     {\r
188                         string strMessage = arStatus.sb.ToString();\r
189                         arStatus.sb.Clear();\r
190 \r
191                         try\r
192                         {\r
193                             JObject jo = Newtonsoft.Json.JsonConvert.DeserializeObject(strMessage) as JObject;\r
194                             string requestId = (string)jo["id"];\r
195 \r
196                             if (!String.IsNullOrEmpty(requestId))\r
197                             {\r
198                                 responses.Add(requestId, strMessage);\r
199 \r
200                                 gotResponse.Set();\r
201                             }\r
202                             else\r
203                             {\r
204                                 // TODO: notifications handling\r
205                                 Console.WriteLine("Notification: {0}", strMessage);\r
206                             }\r
207                         }\r
208                         catch (Newtonsoft.Json.JsonSerializationException e)\r
209                         {\r
210                             // TODO: handle parse error\r
211                         }\r
212                     }\r
213                 }\r
214 \r
215                 arClient.BeginReceive(arStatus.buffer, 0, StratumReadState.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), arStatus);\r
216             };\r
217 \r
218             client.BeginReceive(state.buffer, 0, StratumReadState.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), state);\r
219         }\r
220     }\r
221 \r
222 }