forked from stanislavhacker/OdbCommunicator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
OdbClient.cs
323 lines (272 loc) · 8.67 KB
/
OdbClient.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
using OdbCommunicator.OdbCommon;
using OdbCommunicator.OdbEventArg;
using OdbCommunicator.OdbExceptions;
using OdbCommunicator.OdbCheck;
using OdbCommunicator.OdbQueries;
using OdbCommunicator.OdbSockets;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
using Windows.Networking;
using Windows.Networking.Sockets;
namespace OdbCommunicator
{
public class OdbClient
{
public static int OBD_REFRESH_RATE = 500;
public static bool OBD_REPORTER_ENABLED = true;
#region EVENTS
public event EventHandler<OdbEventArgs> DataReceive;
#endregion
#region PRIVATES
private Boolean? isOdb = null;
private OdbSocket socket = null;
private OdbStatus status = null;
private OdbCommands commands = null;
private OdbEcu ecu = null;
private OdbReporter reporter = new OdbReporter();
private DispatcherTimer poller;
private Dictionary<OdbPid, OdbQuery> queryResponses = new Dictionary<OdbPid, OdbQuery>();
#endregion
#region PROPERTY
/// <summary>
/// Check if is connected
/// </summary>
public bool IsConnected
{
get
{
return this.socket.IsConnected;
}
}
#endregion
/// <summary>
/// Create new odb client
/// </summary>
public OdbClient()
{
this.socket = new OdbSocket(new StreamSocket());
this.status = new OdbStatus(this.socket);
this.commands = new OdbCommands(this.socket);
this.ecu = new OdbEcu(this.socket, this.commands);
}
#region QUERIES
/// <summary>
/// Get data for pid
/// </summary>
/// <param name="pid"></param>
/// <returns></returns>
public OdbQueryResponse RequestFor(OdbPid pid)
{
//add new query to databaze
if (!queryResponses.ContainsKey(pid))
{
OdbQuery newQuery = new OdbQuery();
newQuery.Pid = pid;
newQuery.Status = this.checkSupported(newQuery);
queryResponses.Add(pid, newQuery);
reporter.ReportNewQuery(newQuery);
}
//read data from query
OdbQuery query = queryResponses[pid];
OdbQueryResponse response = new OdbQueryResponse();
if (query.Status == QueryStatus.Complete)
{
response.Data = query.Data;
response.Unit = query.Pid.Units;
response.MinValue = query.Pid.MinValue;
response.MaxValue = query.Pid.MaxValue;
return response;
}
return null;
}
#endregion
#region PUBLIC
/// <summary>
/// Check if is odb device
/// </summary>
/// <returns></returns>
public async Task<bool> IsOdb()
{
if (!isOdb.HasValue)
{
isOdb = await status.IsOdb();
}
return isOdb.Value;
}
/// <summary>
/// Connect
/// </summary>
/// <param name="hostname"></param>
/// <param name="service"></param>
/// <returns></returns>
public async Task Connect(HostName hostname, String service = "{00001101-0000-1000-8000-00805f9b34fb}")
{
await this.socket.Connect(hostname, service);
if (await this.IsOdb())
{
//load supported pids from odb device
await this.commands.LoadSupportedPids();
//load ecu by supported pids
this.ecu.LoadCurrentEcu();
//create poll for reading data from ODB periodicaly
this.createPoller();
}
}
/// <summary>
/// Disconnect
/// </summary>
public void Disconnect()
{
if (this.IsConnected)
{
this.poller.Stop();
this.poller = null;
this.socket.Disconnect();
}
}
/// <summary>
/// Start reading data from device
/// </summary>
public void Start()
{
if (!this.IsConnected)
{
throw new OdbException(OdbError.DeviceIsNotConnected);
}
//trigger data receive
triggerOnDataReceive();
this.poller.Start();
reporter.ReportInfo("Start poller for obtaining data from device.");
}
/// <summary>
/// Stop reading data from device
/// </summary>
public void Stop()
{
this.poller.Stop();
this.poller = null;
reporter.ReportInfo("Stop poller and reading data from device.");
}
#endregion
#region PRIVATE
/// <summary>
/// Create pooler for data reading
/// </summary>
private void createPoller()
{
if (poller != null)
{
return;
}
poller = new DispatcherTimer();
poller.Interval = TimeSpan.FromMilliseconds(OBD_REFRESH_RATE);
poller.Tick += onPollerRequest;
//trigger data receive
triggerOnDataReceive();
}
/// <summary>
/// On poller request
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void onPollerRequest(object sender, EventArgs e)
{
if (queryResponses.Count == 0)
{
return;
}
poller.Stop();
foreach (var q in queryResponses)
{
OdbQuery query = q.Value;
if (query.Status != QueryStatus.NotSupported)
{
OdbResponse response = await this.socket.SendAndCheck(query.Pid);
if (response.IsValid)
{
OdbData data = this.socket.ResolveData(response, query.Pid);
if (data != null)
{
query.Status = QueryStatus.Complete;
query.Data = this.parseDataForSpecifiedPid(query.Pid, data);
}
else
{
query.Status = QueryStatus.Error;
}
}
else
{
query.Status = QueryStatus.Error;
}
}
}
//trigger data receive
triggerOnDataReceive();
poller.Start();
}
/// <summary>
/// Check if query is supported
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
private QueryStatus checkSupported(OdbQuery query)
{
if (this.commands.IsPidSupportedForEcu(this.ecu.Current, query.Pid))
{
return QueryStatus.NoData;
}
return QueryStatus.NotSupported;
}
/// <summary>
/// Parse data for specified pid
/// </summary>
/// <param name="odbPid"></param>
/// <param name="data"></param>
/// <returns></returns>
private Double parseDataForSpecifiedPid(OdbPid odbPid, OdbData data)
{
int A = -1, B = -1, C = -1, D = -1;
int length = data.Data.Length;
if (length > 4 || length == 0)
{
throw new OdbException(OdbError.IncorrectDataLength);
}
if (length >= 4)
{
D = Convert.ToInt32(data.Data[3], 16);
}
if (length >= 3)
{
C = Convert.ToInt32(data.Data[2], 16);
}
if (length >= 2)
{
B = Convert.ToInt32(data.Data[1], 16);
}
if (length >= 1)
{
A = Convert.ToInt32(data.Data[0], 16);
}
return odbPid.Compute(A, B, C, D);
}
/// <summary>
/// Trigger on data receive
/// </summary>
private void triggerOnDataReceive()
{
if (this.DataReceive != null)
{
OdbEventArgs args = new OdbEventArgs();
args.Client = this;
this.DataReceive.Invoke(this, args);
}
}
#endregion
}
}