//----------------------------------------------------------------------- // // Copyright © 2012 Nils Hammar. All rights reserved. // //----------------------------------------------------------------------- /* * Software to access vehicle information via the OBD-II connector. * * Copyright © 2012 Nils Hammar * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Alternative licensing is possible, see the licensing document. * * The above text may not be removed or modified. */ namespace ObdSimulator.Protocol { using System; using System.Collections.Generic; using System.Linq; using System.Text; using global::SharedObjects; using global::SharedObjects.Protocol; /// /// Implements the ISO9141 protocol. /// public class DataParser : IDataParser { /// /// Data buffer. /// private string buf = string.Empty; /// /// Received data without header (payload part). /// private List rxData = new List(); /// /// Checksum Byte. /// private byte checkSum = 0; /// /// Next byte shall be checksum. /// private bool dataComplete = false; /// /// Event logging instance. /// private ILogging iLogging; /// /// 'true' if checksum shall be expected. /// private bool expectChecksum; /// /// Data bytes to expect for complete command. /// private int dataBytes = 0; /// /// List of received data bytes. /// private List dataRecord = new List(); /// /// Array of transmitted data. /// private byte[] txDataArray = null; /// /// Lock object to avoid race conditions. /// private object lockObject = new object(); /// /// Data packet logging. /// private IDataLogging iDataLogging; /// /// Session layer list. /// private IList iSessionLayerList; /// /// Initializes a new instance of the class. /// /// Event logging instance. /// 'true' if checksum shall be expected. /// Data transmitter instance. public DataParser(ILogging iLogging, IDataLogging iDataLogging, IList iSessionLayerList, bool expectChecksum) { this.iLogging = iLogging; this.iDataLogging = iDataLogging; this.iSessionLayerList = iSessionLayerList; this.expectChecksum = expectChecksum; } /// /// Handle dispose of object. /// public void Dispose() { this.Dispose(true); } /// /// Handle dispose of object. /// /// public void Dispose(bool disposing) { } /// /// Parse data. /// /// Character to use in parser. public void parse(byte ch) { this.dataRecord.Add(ch); if (this.expectChecksum && this.dataComplete) { if (this.checkSum != ch) { this.iLogging.appendText("Invalid checksum, calculated=0x" + this.checkSum.ToString("x2") + ", expected=0x" + ch.ToString("x2")); } else { this.activateServiceParser(); } } else { this.checkSum += ch; if (this.buf.Length == 0 && ch == 0x68) { this.checkSum = ch; this.buf += ch.ToString("X2"); } else { if (this.buf.Length == 2 && ch == 0x6a) { this.buf += ch.ToString("X2"); } else { if (this.buf.Length == 4 && ch == 0xf1) { this.buf += ch.ToString("X2"); } else { if (this.buf.Length >= 6) { this.buf += ch.ToString("X2"); this.rxData.Add(ch); if (this.dataBytes == 0) { this.selectService(ch); } if (this.buf.Length == ((this.dataBytes * 2) + 6)) { this.dataComplete = true; } } else { this.buf = string.Empty; this.rxData.Clear(); this.dataBytes = 0; this.dataComplete = false; } } } } if (!this.expectChecksum && this.dataComplete) { this.activateServiceParser(); } } } /// /// Select service depending on byte value. /// /// Service to select. private void selectService(byte ch) { switch (ch) { case 0x01: this.dataBytes = 2; break; case 0x02: this.dataBytes = 3; break; case 0x03: this.dataBytes = 1; break; case 0x04: this.dataBytes = 1; break; case 0x05: this.dataBytes = 3; break; case 0x06: this.dataBytes = 2; break; case 0x07: this.dataBytes = 1; break; case 0x08: this.dataBytes = 7; break; case 0x09: this.dataBytes = 2; break; case 0x0a: this.dataBytes = 1; break; case 0x22: this.dataBytes = 3; break; case 0xAA: this.dataBytes = 1; break; default: this.buf = string.Empty; this.rxData.Clear(); this.dataBytes = 0; this.dataComplete = false; break; } } /// /// Activate service parser and parse payload data. /// private void activateServiceParser() { byte[] ba = this.dataRecord.ToArray(); this.dataRecord.Clear(); lock (this.lockObject) { if (this.txDataArray != null) { int n = ba.Length - this.txDataArray.Length; if (n > 0) { byte[] ba2 = new byte[n]; Array.Copy(ba, this.txDataArray.Length, ba2, 0, n); ba = ba2; } } this.iDataLogging.appendRow("In", ba); } if (this.rxData.Count > 0) { byte[] payload = this.rxData.ToArray(); foreach (ISessionLayer sessionLayer in this.iSessionLayerList) { sessionLayer.receiveData(0x00, payload, 0x00, 0x00, 0x00); } } this.buf = string.Empty; this.rxData.Clear(); this.dataBytes = 0; this.dataComplete = false; } } }