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