UNPKG

obd-raw-data-parser

Version:

A lightweight TypeScript library for parsing OBD-II raw data into human readable format. Based on the excellent work of obd-utils by Nishkalkashyap.

157 lines (156 loc) 5.78 kB
import { CanDecoder } from "./Support/Can.js"; import { NonCanDecoder } from "./Support/NonCan.js"; import { toHexString, formatMessage } from "../utils.js"; import { handleFrameSequence } from "./utils/dtcDecoder.js"; const DTC_MODES = { MODE03: { REQUEST: "03", RESPONSE: 0x43, DIVIDER: 1, NAME: "CURRENT", DESCRIPTION: "Current DTCs", }, MODE07: { REQUEST: "07", RESPONSE: 0x47, DIVIDER: 1, NAME: "PENDING", DESCRIPTION: "Pending DTCs", }, MODE0A: { REQUEST: "0A", RESPONSE: 0x4a, DIVIDER: 1, NAME: "PERMANENT", DESCRIPTION: "Permanent DTCs", }, }; export class DTCBaseDecoder { constructor(config) { const { isCan = false, serviceMode, troubleCodeType, logPrefix } = config; // Set the service mode first so getModeResponseByte() can properly determine the response byte this.serviceMode = serviceMode.toUpperCase(); this.troubleCodeType = troubleCodeType; this.logPrefix = `${logPrefix} [DTC-${isCan ? "CAN" : "NonCAN"}]`; // Get mode response after setting serviceMode const modeResponse = this.getModeResponseByte(); // Use the correct mode response byte for both CAN and non-CAN decoders this.decoder = isCan ? new CanDecoder(modeResponse) : new NonCanDecoder(); if (!isCan) { this.decoder.setModeResponse(modeResponse); } else { this.decoder.setModeResponse(modeResponse); } // Reference the methods rather than binding them to avoid property conflicts const decoderAny = this.decoder; if (typeof decoderAny._log !== "function") { decoderAny._log = this.log.bind(this); } if (typeof decoderAny.setDTC !== "function") { decoderAny.setDTC = this.setDTC.bind(this); } } decodeDTCs(rawResponseBytes) { if (!this.validateServiceMode(this.serviceMode)) { return []; } // Handle frame sequences and normalize const processedFrames = handleFrameSequence(rawResponseBytes); return this.decoder.decodeDTCs(processedFrames); } getRawDTCs() { const rawDtcs = this.decoder.getRawDTCs(); // Convert string DTCs to DTCObject format if needed return rawDtcs.map(dtc => { if (typeof dtc === 'string') { // Convert string DTC to DTCObject format const match = dtc.match(/^([PCBU])(\d)(\d)(\d{2})$/); if (match) { const [, category, d2, d3, d45] = match; return { type: 'PCBU'.indexOf(category), digit2: parseInt(d2), digit3: parseInt(d3), digits45: parseInt(d45, 16) }; } // Return a default DTCObject if string format is invalid return { type: 0, digit2: 0, digit3: 0, digits45: 0 }; } return dtc; }); } parseDTCStatus(statusByte) { // Convert status byte to hex for logging const statusHex = toHexString(statusByte); this.log("debug", `Parsing DTC status: ${statusHex}`); // Extract MIL status const milActive = (statusByte & 0x80) !== 0; // Simple DTC count case - when value is less than 0x20 and MIL is not set if (!milActive && statusByte < 0x20) { return { milActive: false, dtcCount: statusByte, currentError: false, pendingError: false, confirmedError: false, egrSystem: false, oxygenSensor: false, catalyst: false, }; } // Parse individual status bits return { milActive, dtcCount: milActive ? statusByte & 0x7f : statusByte & 0x0f, currentError: (statusByte & 0x20) !== 0, pendingError: (statusByte & 0x10) !== 0, confirmedError: (statusByte & 0x08) !== 0, egrSystem: (statusByte & 0x04) !== 0, oxygenSensor: (statusByte & 0x02) !== 0, catalyst: (statusByte & 0x01) !== 0, }; } getModeResponseByte() { if (!this.serviceMode) { this.log("error", formatMessage(`Invalid service mode: ${this.serviceMode}`, this.logPrefix)); return 0x43; // Default to mode 03 response } const upperMode = this.serviceMode.toUpperCase(); const service = Object.values(DTC_MODES).find((s) => s.REQUEST === upperMode); if (!service) { this.log("error", `Invalid service mode: ${this.serviceMode}`); return 0x43; } return service.RESPONSE; } validateServiceMode(mode) { if (!mode) { this.log("error", `Invalid service mode: ${mode}`); return false; } const upperMode = mode.toUpperCase(); const isValid = Object.values(DTC_MODES).some((service) => service.REQUEST === upperMode); if (!isValid) { this.log("error", `Invalid service mode: ${mode}`); } return isValid; } log(level, ...message) { if (false == false) { //return; } console.log(formatMessage(`[${level}] ${this.logPrefix}`, "", ""), ...message); } setDTC(dtc) { this.log("info", formatMessage(`Setting ${this.troubleCodeType} DTC: ${dtc}`, this.logPrefix)); } }