//----------------------------------------------------------------------- // // 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.OBD { using System; using System.Collections; using System.Collections.Generic; using System.Windows.Forms; using global::SharedObjects; using global::SharedObjects.DataMgmt; using global::SharedObjects.Misc; using global::SharedObjects.Protocol; using global::SharedObjects.Protocol.OBD; /// /// Class for parsing the "mode" byte received from the ECU. /// /// This is the top level of the data structure, and this class /// dispatches to various sub classes depending on data type. /// /// public class ModeParser : IModeParser { /// /// Position of MODE byte in array. /// public const int BYTE_MODE = 0; /// /// Position of PID byte in array. /// public const int BYTE_PID = 1; /// /// Position of error code byte in the array for error responses. /// public const int BYTE_ERRCODE = 2; /// /// Position of Frame# byte. /// public const int BYTE_FRAME = 2; /// /// Current logger instance. /// private ILogging iLogging; /// /// Current protocol handler. /// private IProtocolHandler protocolHandler; /// /// List of panels able to receive and parse data. /// private List dataPanels = new List(); /// /// Dictionary for fast lookup of PidGroup item. /// private Hashtable modePidDictionary = new Hashtable(); /// /// Messaging interface. /// private IMessaging iMessaging; /// /// Gets Current PID parser instance. /// public IPidParser pidParser { get; private set; } /// /// Gets or sets a value indicating whether probing PIDs is active. /// public bool probePids { get; set; } /// /// Data source interface instance. /// private IDataSource iDataSource; /// /// Request data to help with parsing. /// private IRequestData requestData = null; /// /// Helper class that matches requests with responses. /// private IRequestMatcher iRequestMatcher; /// /// Initializes a new instance of the class. /// /// Logger instance. /// Data source interface instance. /// Protocol handler instance. /// PID parser instance. /// Messaging interface instance. /// Current request matcher instance. public ModeParser( ILogging iLogging, IDataSource iDataSource, IProtocolHandler protocolHandler, PidParser pidParser, IMessaging iMessaging, IRequestMatcher iRequestMatcher) { this.iLogging = iLogging; this.iDataSource = iDataSource; this.protocolHandler = protocolHandler; this.pidParser = pidParser; this.iMessaging = iMessaging; this.iRequestMatcher = iRequestMatcher; this.configureModePidDictionary(); if (protocolHandler != null) { protocolHandler.addModeParser(this); } } /// /// Finalizes an instance of the class. /// ~ModeParser() { this.close(); } /// /// Close the service. /// public void close() { if (this.protocolHandler != null) { this.protocolHandler.removeModeParser(this); this.protocolHandler = null; } } /// /// Register a panel able to receive data. /// /// Panel to register. public void registerPanel(IDataPanel panel) { this.dataPanels.Add(panel); } /// /// Remove registered panel. /// /// Panel to unregister. public void unregisterPanel(IDataPanel panel) { this.dataPanels.Remove(panel); } /// /// Receive data to be parsed and presented. /// /// Data to be presented. /// Source address of message. /// Detected protocol by ELM/AGV adapter. public void receiveMessageData(byte[] payload, uint srcAddress, uint detectedProtocol) { bool popNext = false; this.requestData = null; if (payload != null) { byte mode_int = payload[BYTE_MODE]; uint? destinationAddress = DataRequester.getDestinationAddress(this.iMessaging.protocolHandler, srcAddress); /* * Some ECUs allows for periodic transmission of data, and therefore * no matching request exist so we have to take care of those cases * here. */ switch (mode_int) { default: this.requestData = this.iRequestMatcher.getMatchingRequest(destinationAddress, mode_int, payload); break; } this.iMessaging.sendMsg(); if (this.probePids) { popNext = this.handleOtherResponses(mode_int, payload, srcAddress, detectedProtocol); } else { switch (mode_int) { case 0x41: // Show current data. popNext = this.handleBasicPids(mode_int, payload, srcAddress); break; case 0x42: // Show freeze frame data. popNext = this.handleFreezeFramePids(mode_int, payload, srcAddress); break; case 0x44: // DTC Clear Diag Codes. // No action to consider. popNext = true; break; case 0x45: // Test results (Oxygen, Non-CAN). // Not available on CAN. popNext = true; break; case 0x48: // Control operation of onboard component. popNext = true; break; case 0x62: // Extended PIDs popNext = this.handleExtendedPids(mode_int, payload, srcAddress); break; case 0x7f: // Negative Response this.handleNrc(payload); popNext = true; break; default: popNext = this.handleOtherResponses(mode_int, payload, srcAddress, detectedProtocol); break; } } } this.iMessaging.sendMsg(); } /// /// Set the pending requests that can be performed after the current request has had an answer. /// /// List of pending requests. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")] public void setPendingRequests(IList pendingModeRequests) { foreach (IRequestData req in pendingModeRequests) { this.iMessaging.queueMsg(req); } } /// /// Set up a dictionary of the known Mode/PID combinations. /// Notice that the list we use is the list that is filtered depending on preferred units. (Metric or Imperial) /// public void configureModePidDictionary() { this.modePidDictionary = new Hashtable(); foreach (XmlClass.pidgroup pidGroup in this.iDataSource.restrictedPidgroups) { foreach (XmlClass.pidgroup.pidlist item in pidGroup.pids) { uint key = getDictionaryKey(pidGroup.mode_int, item.pid_int); if (!this.modePidDictionary.ContainsKey(key)) { this.modePidDictionary.Add(key, item); } // Since PID 0x02 is identical with PID 0x01 we just copy the list. // For PID 0x22 the first 256 PIDs are identical with PID 0x01, so we copy that too. if (pidGroup.mode_int == 0x01) { uint key02 = getDictionaryKey(0x02, item.pid_int); uint key22 = getDictionaryKey(0x22, item.pid_int); if (!this.modePidDictionary.ContainsKey(key02)) { this.modePidDictionary.Add(key02, item); } if (!this.modePidDictionary.ContainsKey(key22)) { this.modePidDictionary.Add(key22, item); } } } } } /// /// Fetch the matching PID Item for the Mode/PID combination. /// /// Current mode byte value. /// ID of PID to fetch. /// Matching PID item or a temporarily generated PID item if not found. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "int", Justification = "Reviewed.")] public XmlClass.pidgroup.pidlist findPidItem(uint mode_int, uint pid) { XmlClass.pidgroup.pidlist pidItem = null; // Notice that we need to mask off the bit set on responses. uint key = getDictionaryKey((mode_int & 0xbf), pid); if (this.modePidDictionary.ContainsKey(key)) { pidItem = (XmlClass.pidgroup.pidlist)this.modePidDictionary[key]; } // Do fallback handling. Better to show something than to crash. // This can be improved a bit to add some default sensor. if (pidItem == null) { pidItem = new XmlClass.pidgroup.pidlist(); pidItem.pid_int = (uint)pid; pidItem.name = " * UNKNOWN PID " + string.Format("0x{0:x2}", pid) + ", mode=" + string.Format("0x{0:x2}", mode_int) + " * "; this.iLogging.appendText("Received PID '" + string.Format("0x{0:x2}", pid) + "' is not configured.\r\n"); } return pidItem; } /// /// Create lookup key for dictionary searches. /// /// Mode part of key (0x00 to 0xff) /// PID or Address part of key (0x000000 to 0xffffff) /// Composite key value. private static uint getDictionaryKey(uint mode, uint pid) { return ((mode << 24) & 0xff000000) | (pid & 0xffffff); } /// /// Basic PIDs are 8 bits, 0x00 to 0xff. /// /// Current mode byte value. /// Message payload. /// Source address of received data. /// 'true' if it's OK to pop the next message to send and send it. private bool handleBasicPids(byte mode_int, byte[] payload, uint srcAddress) { bool popNext = false; uint pid = (uint)(payload[BYTE_PID] & 0xff); int headerSize = 2; XmlClass.pidgroup.pidlist pidItem = this.findPidItem(mode_int, pid); byte[] pidPayload = Utils.extractData(payload, headerSize); try { popNext = this.pidParser.parsePid(pidItem, mode_int, 0, pidPayload, srcAddress); } catch (IndexOutOfRangeException ex) { this.iLogging.appendText("Insufficient data for PID, mode=0x" + mode_int.ToString("x2") + ", PID=0x" + pidItem.pid_int.ToString("x4") + " - " + pidItem.name + "\r\n" + ex.Message + "\r\n\r\n" + ex.StackTrace + "\r\n"); } return popNext; } /// /// Freeze Frame data are 8 bits PID, 0x00 to 0xff and 8 bits Frame #. /// /// Current mode byte value. /// Message payload. /// Source address of received data. /// 'true' if it's OK to pop the next message to send and send it. private bool handleFreezeFramePids(byte mode_int, byte[] payload, uint srcAddress) { bool popNext = false; uint pid = (uint)(payload[BYTE_PID] & 0xff); byte frame = payload[BYTE_FRAME]; int headerSize = 3; XmlClass.pidgroup.pidlist pidItem = this.findPidItem(mode_int, pid); byte[] pidPayload = Utils.extractData(payload, headerSize); try { popNext = this.pidParser.parsePid(pidItem, mode_int, frame, pidPayload, srcAddress); } catch (IndexOutOfRangeException ex) { this.iLogging.appendText("Insufficient data for PID, mode=0x" + mode_int.ToString("x2") + ", PID=0x" + pidItem.pid_int.ToString("x4") + " - " + pidItem.name + "\r\n" + ex.Message + "\r\n\r\n" + ex.StackTrace + "\r\n"); } return popNext; } /// /// Extended PIDs are 16 bits, 0x0000 to 0xffff. /// Notice that 0x0000 to 0x00ff shall be identical with basic PIDs. /// /// Current mode byte value. /// Message payload. /// Source address of received data. /// 'true' if it's OK to pop the next message to send and send it. private bool handleExtendedPids(byte mode_int, byte[] payload, uint srcAddress) { bool popNext = false; uint pid = (uint)((payload[BYTE_PID] << 8 | payload[BYTE_PID + 1]) & 0xffff); int headerSize = 3; XmlClass.pidgroup.pidlist pidItem = this.findPidItem(mode_int, pid); byte[] pidPayload = Utils.extractData(payload, headerSize); try { popNext = this.pidParser.parsePid(pidItem, mode_int, 0, pidPayload, srcAddress); } catch (IndexOutOfRangeException ex) { this.iLogging.appendText("Insufficient data for PID, mode=0x" + mode_int.ToString("x2") + ", PID=0x" + pidItem.pid_int.ToString("x4") + " - " + pidItem.name + "\r\n" + ex.Message + "\r\n\r\n" + ex.StackTrace + "\r\n"); } return popNext; } /// /// Some panels can have additional functionality that /// is taking care of cases that aren't the default. /// /// Loop through all panels that has registered and break /// on the first panel that has captured the data. /// /// /// Notice that some panels may capture data and not /// tell about it since the data can correctly be /// captured by more than one panel and presented. /// /// /// Current mode byte value. /// Payload array. /// Source address of received data. /// Detected protocol by ELM/AGV adapter. /// 'true' if it's OK to pop the next message to send and send it. private bool handleOtherResponses(byte mode_int, byte[] payload, uint srcAddress, uint detectedProtocol) { bool popNext = true; foreach (IDataPanel iDataPanel in this.dataPanels) { try { if (iDataPanel.parse(mode_int, payload, this.requestData, ref popNext, srcAddress, detectedProtocol)) { // break; } } catch (Exception ex) { this.iLogging.appendText(ex.GetType().ToString() + ": " + ex.Message + "\r\n" + ex.StackTrace + "\r\n"); } } return popNext; } /// /// Handle a negative response from ECU. /// /// This method takes the best effort to try to identify /// the cause and present an explanation to the user. /// /// /// Payload array. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Suitable in this case.")] private void handleNrc(byte[] payload) { uint service = payload[BYTE_PID]; string fault = string.Empty; for (int i = 0; i < this.iDataSource.modes.Count; i++) { if (service == this.iDataSource.modes[i].mode_int) { fault = string.Format("0x{0:x2}", this.iDataSource.modes[i].mode_int) + ": " + this.iDataSource.modes[i].name + "\r\n\r\n"; } } int severity = LogLevel.LOG_WARN; switch (payload[BYTE_ERRCODE]) { case 0x10: fault += "General Reject - Service rejected by ECU.\r\n\r\n" + "This response code indicates that the service is rejected\r\n" + "but the server (ECU) does not specify the reason of the rejection."; break; case 0x11: fault += "Service Not Supported - Service not supported by ECU.\r\n\r\n" + "This response code indicates that the requested action will not\r\n" + "be taken because the server (ECU) does not support the requested service."; break; case 0x12: fault += "Sub Function Not Supported -Invalid Format\r\n\r\n" + "This response code indicates that the requested action will not\r\n" + "be taken because the server (ECU) does not support the arguments of\r\n" + "the request message or the format of the argument bytes do not match\r\n" + "the prescribed format for the specified service."; break; case 0x13: fault += "Incorrect Message Length Or Invalid Format\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "length of the received request message does not match the prescribed length for the\r\n" + "specified service or because the format of the parameters does not match the\r\n" + "prescribed format for the specified service."; break; case 0x14: fault += "Sub Function Not Supported -Invalid Format\r\n\r\n" + "This response code shall be reported by the server if the response to be generated\r\n" + "exceeds the maximum number of bytes available by the underlying network layer.\r\n" + "EXAMPLE This problem may occur when several DIDs are requested at a time and the\r\n" + "combination of all DIDs in the response exceeds the limit of the underlying transport protocol.\r\n"; break; case 0x21: fault += "Busy - Repeat Request\r\nThis response code indicates that the\r\n" + "server (ECU) is temporarily too busy to perform the requested operation.\r\n" + "For ISO 15765-4 protocol the client (external test equipment) shall behave\r\n" + "as defined in ISO 15765-4. In a multi-client (more than one external test\r\n" + "equipment, e.g. telematic client) environment the diagnostic request\r\n" + "message of one client might be blocked temporarily by a negative response\r\n" + "message with response code $21 while another client finishes\r\n" + "a diagnostic task.\r\n\r\n" + "Therefore this negative response code is only allowed to be used during the\r\n" + "initialization sequence of the protocol.\r\n\r\n" + "NOTE If the server (ECU) is able to perform the diagnostic task but needs\r\n" + "additional time to finish the task and prepares the response message,\r\n" + "the negative response message with response code $78 are used instead of $21."; break; case 0x22: fault += "Conditions Not Correct Or Request Sequence Error\r\n\r\n" + "This response code indicates that the requested action will not be taken\r\n" + "because the server (ECU) prerequisite conditions are not met. This request\r\n" + "may also occur when sequence-sensitive requests are issued in the wrong order."; break; case 0x24: fault += "Request Sequence Error\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server expects a different sequence of request messages or message to that sent by\r\n" + "the client. This may occur when sequence-sensitive requests are issued in the wrong\r\n" + "order.\r\n" + "EXAMPLE A successful SecurityAccess service specifies a sequence of requestSeed and\r\n" + "sendKey as sub-functions in the request messages. If the sequence is sent differently by the\r\n" + "client, the server shall send a negative response message with the negative response code\r\n" + "24 hex - requestSequenceError.\r\n"; break; case 0x25: fault += "No Response From Subnet Component\r\n\r\n" + "This response code indicates that the server has received the request but the\r\n" + "requested action could not be performed by the server, as a subnet component\r\n" + "which is necessary to supply the requested information did not respond within the\r\n" + "specified time.\r\n" + "The noResponseFromSubnetComponent negative response shall be implemented\r\n" + "by gateways in electronic systems which contain electronic subnet components and\r\n" + "which do not directly respond to the client’s request. The gateway may receive the\r\n" + "request for the subnet component and then request the necessary information from\r\n" + "the subnet component. If the subnet component fails to respond, the server shall use\r\n" + "this negative response to inform the client about the failure of the subnet component.\r\n" + "This response code is, in general, supported by each diagnostic service, unless\r\n" + "otherwise stated in the data-link-specific implementation document; therefore, it is\r\n" + "not listed in the list of applicable response codes of the diagnostic services.\r\n"; break; case 0x26: fault += "Failure Prevents Execution Of Requested Action\r\n\r\n" + "This response code indicates that the requested action will not be taken because a\r\n" + "failure condition, identified by a DTC (with at least one DTC status bit for TestFailed,\r\n" + "Pending, Confirmed or TestFailedSinceLastClear set to 1), has occurred and that\r\n" + "this failure condition prevents the server from performing the requested action.\r\n" + "This NRC can, for example, direct the technician to read DTCs in order to identify\r\n" + "and fix the problem.\r\n" + "NOTE This implies that diagnostic services used to access DTCs shall not implement this\r\n" + "NRC, as an external test tool may check for the above NRC and automatically request DTCs\r\n" + "whenever the above NRC has been received.\r\n" + "This response code is, in general, supported by each diagnostic service (except the\r\n" + "services mentioned above), unless otherwise stated in the data-link-specific\r\n" + "implementation document; therefore, it is not listed in the list of applicable response\r\n" + "codes of the diagnostic services.\r\n"; break; case 0x31: fault += "Request Out Of Range\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server has detected that the request message contains a parameter which attempts\r\n" + "to substitute a value beyond its range of authority (e.g. attempting to substitute a\r\n" + "data byte of 111 when the data is only defined to 100), or which attempts to access a\r\n" + "dataIdentifier/routineIdentifer that is not supported or not supported in active session.\r\n" + "This response code shall be implemented for all services which allow the client to\r\n" + "read data, write data or adjust functions by data in the server.\r\n"; break; case 0x33: fault += "Security Access Denied\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server’s security strategy has not been satisfied by the client.\r\n" + "The server shall send this response code if one of the following cases occurs:\r\n" + " - the test conditions of the server are not met;\r\n" + " - the required message sequence, e.g. DiagnosticSessionControl, securityAccess, is not met;\r\n" + " - the client has sent a request message which requires an unlocked server.\r\n" + "Besides the mandatory use of this negative response code as specified in the\r\n" + "applicable services within this part of ISO 14229, this negative response code can\r\n" + "also be used for any case where security is required and is not yet granted to\r\n" + "perform the required service.\r\n"; break; case 0x35: fault += "Invalid Key\r\n\r\n" + "This response code indicates that the server has not given security access because\r\n" + "the key sent by the client did not match with the key in the server’s memory. This\r\n" + "counts as an attempt to gain security. The server shall remain locked and increment\r\n" + "its internal securityAccessFailed counter.\r\n"; break; case 0x36: fault += "Exceed Number Of Attempts\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "client has unsuccessfully attempted to gain security access more times than the\r\n" + "server’s security strategy will allow.\r\n"; break; case 0x37: fault += "Required Time Delay Not Expired\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "client’s latest attempt to gain security access was initiated before the server’s\r\n" + "required timeout period had elapsed.\r\n"; break; case 0x70: fault += "Upload Download Not Accepted\r\n\r\n" + "This response code indicates that an attempt to upload/download to a server’s\r\n" + "memory cannot be accomplished due to fault conditions.\r\n"; break; case 0x71: fault += "Transfer Data Suspended\r\n\r\n" + "This response code indicates that a data transfer operation was halted due to a fault.\r\n" + "The active transferData sequence shall be aborted.\r\n"; break; case 0x72: fault += "General Programming Failure\r\n\r\n" + "This response code indicates that the server detected an error when erasing or\r\n" + "programming a memory location in the permanent memory device (e.g. Flash Memory).\r\n"; break; case 0x73: fault += "Wrong Block Sequence Counter\r\n\r\n" + "This response code indicates that the server detected an error in the sequence of\r\n" + "blockSequenceCounter values. Note that the repetition of a TransferData request\r\n" + "message with a blockSequenceCounter equal to the one included in the previous\r\n" + "TransferData request message shall be accepted by the server.\r\n"; break; case 0x78: severity = LogLevel.LOG_INFO; fault += "Request Correctly Received - Response Pending\r\n\r\n" + "This response code indicates that the request message was received\r\n" + "correctly, and that any parameters in the request message were valid,\r\n" + "but the action to be performed may not be completed yet. This response\r\n" + "code can be used to indicate that the request message was properly\r\n" + " received and does not need to be re-transmitted,\r\n" + "but the server (ECU) is not yet ready to receive another request.\r\n\r\n" + "The negative response message with this response code may be repeated\r\n" + "by the ECU(s) within P2K-Line = P2CAN = P2*max until the positive response\r\n" + "message with the requested data is available."; break; case 0x7E: fault += "Sub Function Not Supported In Active Session\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server does not support the requested sub-function in the session currently active.\r\n" + "Within the programmingSession, negative response code SFNS\r\n" + "(subFunctionNotSupported) may optionally be reported instead of negative response\r\n" + "code SNFSIAS (subFunctionNotSupportedInActiveSession). This response code\r\n" + "shall only be used when the requested sub-function is known to be supported in\r\n" + "another session, otherwise response code SFNS (subFunctionNotSupported) shall\r\n" + "be used.\r\n" + "This response code shall be supported by each diagnostic service with a subfunction\r\n" + "parameter, if not otherwise stated in the data-link-specific implementation\r\n" + "document; therefore, it is not listed in the list of applicable response codes of the\r\n" + "diagnostic services.\r\n"; break; case 0x7F: fault += "Service Not Supported In Active Session\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server does not support the requested service in the session currently active. This\r\n" + "response code shall only be used when the requested service is known to be\r\n" + "supported in another session, otherwise response code SNS (serviceNotSupported)\r\n" + "shall be used.\r\n" + "This response code is, in general, supported by each diagnostic service, unless\r\n" + "otherwise stated in the data-link-specific implementation document; therefore, it is\r\n" + "not listed in the list of applicable response codes of the diagnostic services.\r\n"; break; case 0x81: fault += "Rpm Too High\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for RPM is not met (current RPM is above a\r\n" + "preprogrammed maximum threshold).\r\n"; break; case 0x82: fault += "Rpm Too Low\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for RPM is not met (current RPM is below a\r\n" + "preprogrammed minimum threshold).\r\n"; break; case 0x83: fault += "Engine Is Running\r\n\r\n" + "This is required for those actuator tests which cannot be actuated while the engine is\r\n" + "running. This is different from the RPM too high negative response and needs to be\r\n" + "allowed.\r\n"; break; case 0x84: fault += "Engine Is Not Running\r\n\r\n" + "This is required for those actuator tests which cannot be actuated unless the Engine\r\n" + "is running. This is different from the RPM too low negative response and shall be allowed.\r\n"; break; case 0x85: fault += "Engine Run Time Too Low\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for engine run time is not met (current engine run time\r\n" + "is below a preprogrammed limit).\r\n"; break; case 0x86: fault += "Temperature Too High\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for temperature is not met (current temperature is\r\n" + "above a preprogrammed maximum threshold).\r\n"; break; case 0x87: fault += "Temperature Too Low\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for temperature is not met (current temperature is below\r\n" + "a preprogrammed minimum threshold).\r\n"; break; case 0x88: fault += "Vehicle Speed Too High\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for vehicle speed is not met (current VS is above a\r\n" + "preprogrammed maximum threshold).\r\n"; break; case 0x89: fault += "Vehicle Speed Too Low\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for vehicle speed is not met (current VS is below a\r\n" + "preprogrammed minimum threshold).\r\n"; break; case 0x8A: fault += "Throttle/Pedal Too High\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for throttle/pedal position is not met (current TP/APP is\r\n" + "above a preprogrammed maximum threshold).\r\n"; break; case 0x8B: fault += "Throttle/Pedal Too Low\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for throttle/pedal position is not met (current TP/APP is\r\n" + "below a preprogrammed minimum threshold).\r\n"; break; case 0x8C: fault += "Transmission Range Not In Neutral\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for being in neutral is not met (current transmission\r\n" + "range is not in neutral).\r\n"; break; case 0x8D: fault += "transmissionRangeNotInGear\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for being in gear is not met (current transmission range\r\n" + "is not in gear).\r\n"; break; case 0x8F: fault += "Brake Switch(es) Not Closed (brake pedal not pressed or not applied)\r\n\r\n" + "For safety reasons, this is required before beginning certain tests, and must be\r\n" + "maintained for the entire duration of the test.\r\n"; break; case 0x90: fault += "Shifter Lever Not In Park\r\n\r\n" + "For safety reasons, this is required before beginning certain tests, and must be\r\n" + "maintained for the entire duration of the test.\r\n"; break; case 0x91: fault += "Torque Converter Clutch Locked\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for torque converter clutch is not met (current TCC\r\n" + "status is above a preprogrammed limit or locked).\r\n"; break; case 0x92: fault += "Voltage Too High\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for voltage at the primary pin of the server (ECU) is not\r\n" + "met (current voltage is above a preprogrammed maximum threshold).\r\n"; break; case 0x93: fault += "Voltage Too Low\r\n\r\n" + "This response code indicates that the requested action will not be taken because the\r\n" + "server prerequisite condition for voltage at the primary pin of the server (ECU) is not\r\n" + "met (current voltage is below a preprogrammed maximum threshold).\r\n"; break; default: fault += " * UNKNOWN * code=" + string.Format("0x{0:x2}", payload[BYTE_ERRCODE]); break; } if (!this.probePids) { if (severity >= LogLevel.LOG_WARN) { MessageBox.Show( fault, "ECU Error Response!", MessageBoxButtons.OK, MessageBoxIcon.Hand); } } this.iLogging.appendText(fault); } } }