/// <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; }
/// <summary> /// Report deleted query /// </summary> /// <param name="pid"></param> public void ReportDeleteQuery(OdbPid pid) { if (!OdbClient.OBD_REPORTER_ENABLED) { return; } Debug.WriteLine(InfoPrefix + "Query for '{0} ({1})' has been removed from register queries.", pid.Description, pid.Pid); }
/// <summary> /// Get supp /// </summary> /// <param name="what"></param> /// <returns></returns> private async Task registerSupportedPid(OdbPid what) { OdbResponse response = await this.socket.SendAndCheck(what); OdbData data = this.socket.ResolveData(response, what); if (data != null) { this.decodeSupportedPidsAndRegisterIt(what, data); } }
/// <summary> /// Decode supported pids and register it /// </summary> /// <param name="what"></param> /// <param name="data"></param> private void decodeSupportedPidsAndRegisterIt(OdbPid what, OdbData data) { List<int> pids = new List<int>(); int pid = Convert.ToInt32(what.Pid.Split(' ')[1], 16); for (int i = 0; i < data.Data.Length; i++) { char[] binary = Convert.ToString(Convert.ToInt32(data.Data[i], 16), 2).ToCharArray(); for (int j = 0; j < binary.Length; j++) { pid++; if (binary[j] == '1') { pids.Add(pid); } } } this.RegisterSupportedPids(data.EcuIdentifier(), data.Protocol, pids); }
/// <summary> /// Clear response message /// </summary> /// <param name="response"></param> /// <returns></returns> private string clearResponse(string response, OdbPid what) { response = response.Replace("\r", " "); String[] lines = response.Split('\n'); for (int i = lines.Length - 1; i >= 0; i--) { String line = lines[i]; line = this.clearLine(what, line); if (line.Length > 0) { return line; } } return this.clearLine(what, lines.Last()); }
/// <summary> /// Resolve incoming data and setup odb data /// </summary> /// <param name="response"></param> /// <param name="what"></param> /// <returns></returns> public OdbData ResolveData(String response, OdbPid what) { int counter = 0; String[] bytes = response.Split(' '); bytes = this.validateReponseBytes(bytes); OdbData data = OdbPids.GetResponseFormatForProtocolNumber(bytes.Length, what.ByteCount); if (bytes.Length < what.ByteCount) { return null; } try { data.Protocol = this.SelectedProtocol; for (int i = 0; i < data.Header.Length; i++) { data.Header[i] = bytes[counter]; counter++; } for (int i = 0; i < data.Info.Length; i++) { data.Info[i] = bytes[counter]; counter++; } for (int i = 0; i < data.Data.Length; i++) { data.Data[i] = bytes[counter]; counter++; } for (int i = 0; i < data.Ender.Length; i++) { data.Ender[i] = bytes[counter]; counter++; } } catch { return null; } return data; }
/// <summary> /// resolve data /// </summary> /// <param name="response"></param> /// <param name="what"></param> /// <returns></returns> public OdbData ResolveData(OdbResponse response, OdbPid what) { return this.ResolveData(response.Response, what); }
/// <summary> /// Is valid response from device /// </summary> /// <param name="response"></param> /// <returns></returns> private bool isValidResponse(String response, OdbPid what) { var lowerResponse = response.ToLowerInvariant(); var rightLength = lowerResponse.Trim().Length > 0; var containsError = lowerResponse.Contains("error"); var containsUnknownChars = lowerResponse.Trim() == ">"; var containsNoData = lowerResponse.Contains("no data") || lowerResponse.Contains("?"); //no data is valid response if (containsNoData) { return true; } //if is data command then validate data length if (what.IsDataCommand && this.ResolveData(response, what) == null) { return false; } //for normal command return rightLength && !containsError && !containsUnknownChars; }
/// <summary> /// Receive data from device /// </summary> /// <param name="what"></param> /// <param name="start"></param> /// <returns></returns> private async Task<OdbResponse> receiveDataFromDevice(OdbPid what, DateTime start) { //await for response await Task.Delay(this.getReponseByPid(what)); //create response OdbResponse odbResponse = new OdbResponse(); odbResponse.Pid = what; try { //reader uint loaded = await reader.LoadAsync(BUFFER_STEP); String response = reader.ReadString(reader.UnconsumedBufferLength); while (loaded == BUFFER_STEP) { loaded = await reader.LoadAsync(BUFFER_STEP); response += reader.ReadString(reader.UnconsumedBufferLength); } odbResponse.Response = this.clearResponse(response, what); odbResponse.IsValid = this.isValidResponse(response, what); } catch { odbResponse.Response = ""; odbResponse.IsValid = false; } odbResponse.Time = DateTime.Now.Subtract(start); return odbResponse; }
/// <summary> /// Send message and check response /// </summary> /// <param name="what"></param> /// <returns></returns> public async Task<OdbResponse> SendAndCheck(OdbPid what) { OdbResponse response = await this.Send(what); if (!response.Response.Contains(what.ExpectedResponse)) { throw new OdbException(OdbError.WrongResponseFromDevice); } return response; }
/// <summary> /// Check if pid is supported for ecu /// </summary> /// <param name="ecu"></param> /// <param name="pid"></param> /// <returns></returns> public Boolean IsPidSupportedForEcu(Ecu ecu, OdbPid pid) { if (supportedPids.ContainsKey(ecu.EcuId)) { var modes = supportedPids[ecu.EcuId]; if (modes.ContainsKey(pid.Mode)) { var pids = modes[pid.Mode]; return pids.Contains(pid.GetPidIdInDecimal()); } } return false; }
/// <summary> /// Clear response message /// </summary> /// <param name="response"></param> /// <returns></returns> private string clearResponse(string response, OdbPid what) { response = response.Replace("\r", " "); String[] lines = response.Split('\n'); for (int i = lines.Length - 1; i >= 0; i--) { String line = lines[i]; line = line.Replace("\n", " "); line = line.Replace(">", ""); line = line.Replace(what.Pid, ""); line = line.Trim(); if (line.Length > 0) { return line; } } return lines.Last().Trim(); }
/// <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> /// Get response for /// </summary> /// <param name="client"></param> /// <param name="odbPid"></param> /// <param name="action"></param> private void getResponseFor(OdbClient client, OdbPid odbPid, Action<Double> action) { var response = client.RequestFor(odbPid); if (response != null) { action(response.Data); } }
/// <summary> /// Register supported pids /// </summary> /// <param name="ecuIdentifier"></param> /// <param name="odbPid"></param> /// <param name="pids"></param> private void RegisterSupportedPids(int ecuIdentifier, OdbPid odbPid, List<int> pids) { if (!supportedPids.ContainsKey(ecuIdentifier)) { supportedPids.Add(ecuIdentifier, new Dictionary<OdbPid, List<int>>()); } Dictionary<OdbPid, List<int>> supportedPidsForEcu = supportedPids[ecuIdentifier]; if (supportedPidsForEcu.ContainsKey(odbPid)) { var pidsSupported = supportedPidsForEcu[odbPid]; supportedPidsForEcu[odbPid] = pidsSupported.Concat(pids).ToList(); } else { supportedPidsForEcu.Add(odbPid, pids); } }
/// <summary> /// Clear line /// </summary> /// <param name="what"></param> /// <param name="line"></param> /// <returns></returns> private string clearLine(OdbPid what, String line) { line = line.Replace("\n", " "); line = line.Replace(">", ""); line = line.Replace(what.Pid, ""); line = line.Trim(); return line; }
/// <summary> /// Send message, return bytes response /// </summary> /// <param name="what"></param> /// <returns></returns> public async Task<OdbResponse> Send(OdbPid what) { if (!this.IsConnected) { throw new OdbException(OdbError.DeviceIsNotConnected); } //timer DateTime start = DateTime.Now; //send try { writer.WriteString(what.Pid + "\r\r"); await writer.StoreAsync(); await writer.FlushAsync(); } catch { this.IsConnected = false; throw new OdbException(OdbError.DeviceIsNotConnected); } //receive data from device OdbResponse odbResponse = await receiveDataFromDevice(what, start); //try again on error reponse while (!odbResponse.IsValid && this.tryCount > 0) { reporter.ReportInfo("Incorrect response '" + odbResponse.Response + "' from device. Try another request. Current try step is " + this.tryCount + "."); odbResponse = await receiveDataFromDevice(what, start); this.tryCount--; } this.tryCount = RESPONSE_TRY_COUNT; //report response to console reporter.ReportResponse(odbResponse); return odbResponse; }
/// <summary> /// Get response time what for pid /// </summary> /// <param name="what"></param> /// <returns></returns> private int getReponseByPid(OdbPid what) { if (what.IsElmCommand) { return 500; } return 250; }
/// <summary> /// Get data for pid /// </summary> /// <param name="pid"></param> /// <returns></returns> public void UnregisterQuery(OdbPid pid) { //remove query from databaze if (queryResponses.ContainsKey(pid)) { queryResponses.Remove(pid); reporter.ReportDeleteQuery(pid); } }