//----------------------------------------------------------------------- // // 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 Protocol { using System.Threading; using global::DeviceApi; using global::SharedObjects; using global::SharedObjects.Api; using global::SharedObjects.GUI; using global::SharedObjects.Misc; using global::SharedObjects.Protocol; /// /// Handler for ISO9141, ISO14230 and KWP2000 /// /// UNDER DEVELOPMENT! /// /// public class Protocol_ISO14230 : Protocol_Common, IProtocolHandler, IMsgCallback { /// /// Start Communication. /// private const int SERVICEID_STARTCOMM = 0x81; /// /// Stop Communication. /// private const int SERVICEID_STOPCOMM = 0x82; /// /// Get Timing Parameters. /// private const int SERVICEID_GET_TIMING_PARAMS = 0x83; /// /// Negative Response. (usually handled by mode parser). /// private const int SERVICEID_NRC = 0x7F; // Same NRC as for other requests. /// /// Start Communication Response. /// private const int SERVICEID_STARTCOMM_RESPONSE = 0xC1; /// /// Stop Communication Response. /// private const int SERVICEID_STOPCOMM_RESPONSE = 0xC2; /// /// Get Timing Parameters Response. /// private const int SERVICEID_GET_TIMING_PARAMS_RESPONSE = 0xC3; /// /// Protocol not yet detected. /// private const int PROTOCOL_UNKNOWN = 0x00; /// /// Protocol ISO9141 detected. /// private const int PROTOCOL_ISO9141 = 0x01; /// /// Protocol ISO14230 (KWP2000) detected. /// private const int PROTOCOL_ISO14230 = 0x02; /// /// Possible address bit alternatives for protocol. /// private static readonly string[] possibleAddressbits = { "8" }; /* /// /// Array of protocol names. /// private static readonly string[] possibleProtocols = { "Unknown", "ISO9141", "ISO14230" }; */ /* /// /// Header modes for data. /// private static readonly string[] headerModes = { "HM0: ", "HM1: ", "HM2: Physical Target Address", "HM3: Functional Target Address" }; */ /// /// Message ID for keep alive message. /// private int keepAliveMsgId; /// /// Gets Which protocol that was detected. /// public int detectedProtocol { get; private set; } /// /// Gets or sets a value indicating whether the long header shall be used even for short messages. /// public bool forceLongHeader { get; set; } /// /// Initializes a new instance of the class. /// /// Logging interface. /// Current protocol. /// Current message handler instance. /// Source address. /// Checksum flag. /// Callback interface for enabling the buttons when a protocol is fully initialized. public Protocol_ISO14230( ILogging iLogging, Protocols protocol, MessageHandler messageHandler, uint srcAddr, bool checksum, IEnableButtons iEnableButtons) : base(iLogging, protocol, messageHandler, srcAddr, checksum, iEnableButtons) { this.iLogging.appendText("Protocol_ISO14230 & ISO9141: srcAddr=0x" + srcAddr.ToString("x2") + ", checksum=" + checksum + "\r\n"); this.detectedProtocol = PROTOCOL_UNKNOWN; this.forceLongHeader = false; // Register this protocol handler with the message handler. this.messageHandler.setProtocolCallback(this); } /// /// Get the address bit alternatives for the protocol. /// /// Array of alternatives. public static string[] addressBitAlternatives() { return possibleAddressbits; } /// /// Indicates if protocol is in 29 bit mode. /// /// 'true' if protocol is in 29 bit mode. public bool is29bit() { return false; } /// /// Initialize protocol. /// /// TX Flags for any messages that are created during initialization. public void init(int txFlags) { uint outBytes = 0; uint keyWord1 = 0; uint keyWord2 = 0; string errtext; PassThruConstants.resultCode ret = PassThruConstants.resultCode.ERR_SUCCESS; if (ret != 0 || (keyWord1 == 0 && keyWord2 == 0)) { iLogging.appendText("init(FIVE_BAUD_INIT)\r\n"); ret = this.messageHandler.IoctlFiveBaudInit(0x33, out outBytes, out keyWord1, out keyWord2); iLogging.appendText("FIVE_BAUD_INIT: ret=" + ret + "[" + J2534_Error.getErrorName(ret) + "], outBytes=" + outBytes + ", kw1=0x" + keyWord1.ToString("x2") + ", kw2=0x" + keyWord2.ToString("x2") + "\r\n"); } if (this.protocol.id == Protocols.ISO14230 && (ret != 0 || (keyWord1 == 0 && keyWord2 == 0))) { Thread.Sleep(400); iLogging.appendText("init(FAST_INIT)\r\n"); IPassThruMsg result; ret = this.messageHandler.IoctlFastInit(this.protocol.id, this.srcAddr, out result); iLogging.appendText("FAST_INIT: ret=" + ret + "[" + J2534_Error.getErrorName(ret) + "]\r\n"); iLogging.dispMsg("FAST_INIT", result); this.messageHandler.errTestConnection(ret, out errtext); iLogging.appendText(errtext + "\r\n"); } if (this.messageHandler.errTestConnection(ret, out errtext)) { uint keyword = keyWord1 << 8 | keyWord2; switch (keyword) { case 0x0808: case 0x9494: this.detectedProtocol = PROTOCOL_ISO9141; this.iLogging.appendText("ISO 9141 detected.\r\n"); this.start(); break; case 0x8fe9: case 0x8f6b: case 0x8f6d: case 0x8fef: this.detectedProtocol = PROTOCOL_ISO14230; this.iLogging.appendText("ISO 14230 detected.\r\n"); this.start(); break; default: this.iLogging.appendText("Unsupported keyword: 0x" + keyword.ToString("x4") + ".\r\n"); break; } } else { iLogging.appendText(errtext + "\r\n"); throw new ProtocolInitException("Connection Failed."); } } /// /// Start protocol. /// public void start() { IPassThruMsg msg = PassThruMsg.getMaskedMsg(this.protocol); if (this.detectedProtocol == PROTOCOL_ISO9141) { iLogging.appendText("start(PROTOCOL_ISO9141)\r\n"); // addPayload(new byte[] { 0x68, 0x6a, 0xf0, SERVICEID_STARTCOMM }, ref msg); // addPayload(new byte[] { 0x68, 0x6a, 0x3f, SERVICEID_STARTCOMM }, ref msg); addPayload(new byte[] { 0x68, 0x6a, 0xf1, SERVICEID_STARTCOMM }, ref msg); } else { iLogging.appendText("start(PROTOCOL_ISO14230)\r\n"); addPayload(new byte[] { 0xc1, 0x33, 0xf1, SERVICEID_STARTCOMM }, ref msg); } PassThruConstants.resultCode res = this.messageHandler.startPeriodicMessage(msg, out this.keepAliveMsgId); this.iLogging.appendText("Keep alive msg started, res=" + res + ", msgId=" + this.keepAliveMsgId + "\r\n"); this.iEnableButtons.enableButtons(); } /// /// Stop protocol. /// public void stopProtocol() { this.messageHandler.stopPeriodicMessage(this.keepAliveMsgId); } /// /// Receive one message and dispatch it to receivers. /// /// Message to receive. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")] public void receiveMessage(IPassThruMsg rxmsg) { int headerMode; int dataLength; int dataStart; int destination; int source; getHeaderData(rxmsg, out headerMode, out dataLength, out dataStart, out destination, out source); // Calculate Checksum if the checkbox is checked. // Additional flags may need to be checked too. int expectedCs = 0; int calculatedCs = 0; if (this.checksum) { expectedCs = (rxmsg.Data[rxmsg.DataSize - 1] & 0xff); calculatedCs = Utils.getChecksum(rxmsg.Data, 0, rxmsg.DataSize - 1); if (expectedCs != calculatedCs) { this.iLogging.appendText("Checksum mismatch detected: expectedCs=" + expectedCs + ", calculatedCs=" + calculatedCs); } } // Get the Service Identification. if (dataStart < rxmsg.DataSize) { // int sid = rxmsg.Data[dataStart]; /* this.iLogging.appendText(headerModes[headerMode] + ", From=0x" + source.ToString("x2") + ", To=0x" + destination.ToString("x2") + ", dataStart=" + dataStart + ", dataLength=" + dataLength + ", Sid=0x" + sid.ToString("x2") + ", expectedCs=0x" + expectedCs.ToString("x2") + ", calculatedCs=0x" + calculatedCs.ToString("x2") + "\r\n" ); this.iLogging.dispMsg("Rx", rxmsg); */ // Extract the payload. byte[] payload = extractPayload(rxmsg, dataStart); this.dispatchMessage(rxmsg, payload, (uint)source, 0); } else { this.iLogging.appendText("Data start is beyond end of received data.\r\n", LogLevel.LOG_WARN); } } /// /// Get payload part from message. /// /// Message to get payload data from. /// Payload byte array. public override byte[] getPayload(IPassThruMsg msg) { int headerMode; int dataLength; int dataStart; int destination; int source; getHeaderData(msg, out headerMode, out dataLength, out dataStart, out destination, out source); byte[] payload = extractPayload(msg, dataStart); return payload; } /// /// Send one message where the header info already is set in the payload. /// /// Source address (address of tester). /// Tx Flags /// Message Payload which includes address data. public void sendProbeMessage(uint srcAddr, int txFlags, byte[] payload) { if (this.detectedProtocol == PROTOCOL_ISO9141) { this.sendMessage(0x3f, 0x33, txFlags, payload); } else { this.sendMessage(0xf0, 0x33, txFlags, payload); } //// sendMessage(0xf1, 0x33, txFlags, payload); } /// /// Send one message to the given receiver address. /// /// Source address. /// Destination address. /// TX Flags. /// Payload data. /// Error message in case of a problem. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "3", Justification = "Reviewed.")] public string sendMessage(uint sourceAddress, uint destinationAddress, int txFlags, byte[] payload) { IPassThruMsg txmsg = PassThruMsg.getMaskedMsg(this.protocol); bool functionalTargetAddressing = false; switch (this.detectedProtocol) { case PROTOCOL_ISO9141: destinationAddress = 0x6a; break; case PROTOCOL_ISO14230: functionalTargetAddressing = (destinationAddress == 0x33); break; } // Add Address. this.setAddress(ref txmsg, sourceAddress, destinationAddress); // FIXME! this.setHeader(ref txmsg, (uint)payload.Length, functionalTargetAddressing); addPayload(payload, ref txmsg); PassThruConstants.resultCode res = this.sendMessage(txFlags, txmsg); // Check if error. string errtext = string.Empty; if (!this.messageHandler.errTestConnection(res, out errtext)) { this.iLogging.dispMsg("Err", txmsg); } return errtext; } /// /// Set additional header data in addition to the address. /// /// Message to add data to. /// Size of message. /// 'true' if the destination address is functional address. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")] public void setHeader(ref IPassThruMsg txmsg, uint size, bool functionalTargetAddressing) { switch (this.detectedProtocol) { case PROTOCOL_ISO9141: txmsg.Data[0] = 0x68; txmsg.DataSize = 3; break; case PROTOCOL_ISO14230: this.setISO14230Header(ref txmsg, size, functionalTargetAddressing); break; } } /// /// Set the address(es) of the message. /// /// Message to set data in. /// Source Address. /// Destination Address. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")] public void setAddress(ref IPassThruMsg txmsg, uint sourceAddress, uint destinationAddress) { txmsg.Data[1] = (byte)(destinationAddress & 0xff); txmsg.Data[2] = (byte)(sourceAddress & 0xff); txmsg.DataSize = 3; } /// /// Set the address(es) of the message. /// /// This is a fully composed address, usually used by filters. /// /// /// Message to set data in. /// Combined Address. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")] public void setAddress(ref IPassThruMsg txmsg, uint address) { txmsg.Data[1] = (byte)((address >> 8) & 0xff); txmsg.Data[2] = (byte)(address & 0xff); txmsg.DataSize = 3; } /// /// Get header data from message. /// /// Message to get header data from. /// Header mode value. /// Data length. /// Start position for data. /// Destination address. /// Source address. private static void getHeaderData(IPassThruMsg rxmsg, out int headerMode, out int dataLength, out int dataStart, out int destination, out int source) { headerMode = ((rxmsg.Data[0] >> 6) & 0xff); dataLength = (rxmsg.Data[0] & 0x3f); dataStart = 3; // If the length was zero we have a 4 byte header with the length // in the 4th byte. if (dataLength == 0) { dataLength = (rxmsg.Data[3] & 0xff); dataStart = 4; } destination = rxmsg.Data[1]; // Should be 'our' address when we are receiving. source = rxmsg.Data[2]; // Should be ECU address when we are receiving. } /// /// Add the payload to the message. /// /// Byte array of payload to send. /// Message to update with payload. /// Hex string with message, to be used for logging. private static string addPayload(byte[] payload, ref IPassThruMsg txmsg) { string dataStr = string.Empty; for (int i = 0; i < payload.Length; i++) { txmsg.Data[txmsg.DataSize++] = payload[i]; dataStr += string.Format("{0:x2} ", payload[i]); } return dataStr; } /// /// Call the message handler and send the formatted message structure. /// /// TX Flags. /// Message to send. /// Status code, 0=success. private PassThruConstants.resultCode sendMessage(int txFlags, IPassThruMsg txmsg) { // Set message flags. txmsg.TxFlags = txFlags; this.addChecksum(ref txmsg); return this.messageHandler.sendMessage(txmsg); } /// /// Add checksum if requested. /// Notice that CAN and ISO 15765 doesn't need a checksum. /// But it's up to the user to decide, so we just check /// if the checkbox is checked. /// /// Message to add checksum to. private void addChecksum(ref IPassThruMsg txmsg) { if (this.checksum) { int cs = Utils.getChecksum(txmsg.Data, 0, txmsg.DataSize); txmsg.Data[txmsg.DataSize++] = (byte)(cs & 0xff); } } /// /// Set additional ISO14230 header data in addition to the address. /// /// Message to add data to. /// Size of message. /// 'true' if the destination address is functional address. private void setISO14230Header(ref IPassThruMsg txmsg, uint size, bool functionalTargetAddressing) { int headerMode; if (functionalTargetAddressing) { headerMode = 3; } else { headerMode = 2; } txmsg.Data[0] = (byte)((headerMode << 6) & 0xc0); if (size >= 64 || this.forceLongHeader) { txmsg.Data[3] = (byte)(size & 0xff); txmsg.DataSize = 4; } else { txmsg.Data[0] |= (byte)(size & 0x3f); txmsg.DataSize = 3; } } } }