react-native-obd-retriver
Version:
A React Native hook library to manage Bluetooth Low Energy connections and communication with ELM327 OBD-II adapters.
2 lines • 20.4 kB
JavaScript
var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:true});exports.VINRetriever=void 0;var _asyncToGenerator2=_interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));var _classCallCheck2=_interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));var _createClass2=_interopRequireDefault(require("@babel/runtime/helpers/createClass"));var _logger=require("../../utils/logger");var _constants=require("../utils/constants");var _helpers=require("../utils/helpers");var PROTOCOL_TYPES={CAN:'CAN',KWP:'KWP',ISO9141:'ISO9141',J1850:'J1850',UNKNOWN:'UNKNOWN'};var HEADER_FORMATS={CAN_11BIT:'11bit',CAN_29BIT:'29bit',KWP:'kwp',ISO9141:'iso9141',J1850:'j1850',UNKNOWN:'unknown'};var PROTOCOL_STATES={INITIALIZED:'INITIALIZED',CONFIGURING:'CONFIGURING',READY:'READY',ERROR:'ERROR'};var VINRetriever=exports.VINRetriever=function(){function VINRetriever(sendCommand){(0,_classCallCheck2.default)(this,VINRetriever);this.mode=VINRetriever.SERVICE_MODE.REQUEST;this.isCan=false;this.protocolNumber=_constants.PROTOCOL.AUTO;this.protocolType=PROTOCOL_TYPES.UNKNOWN;this.headerFormat=HEADER_FORMATS.UNKNOWN;this.ecuResponseHeader=null;this.protocolState=PROTOCOL_STATES.INITIALIZED;this.isHeaderEnabled=false;this.sendCommand=sendCommand;}return(0,_createClass2.default)(VINRetriever,[{key:"delay",value:function delay(ms){return new Promise(function(resolve){setTimeout(resolve,ms);});}},{key:"_configureAdapterForVIN",value:(function(){var _configureAdapterForVIN2=(0,_asyncToGenerator2.default)(function*(){void _logger.log.info(`[${this.constructor.name}] Configuring adapter for VIN retrieval...`);this.protocolState=PROTOCOL_STATES.CONFIGURING;try{yield this.sendCommand(_constants.ELM_COMMANDS.RESET);yield this.delay(_constants.DELAYS_MS.RESET);}catch(error){void _logger.log.warn(`[${this.constructor.name}] Reset warning:`,{error:error instanceof Error?error.message:String(error)});}var setupCommands=[{cmd:_constants.ELM_COMMANDS.ECHO_OFF,desc:'Disable echo'},{cmd:_constants.ELM_COMMANDS.LINEFEEDS_OFF,desc:'Disable linefeeds'},{cmd:_constants.ELM_COMMANDS.SPACES_OFF,desc:'Disable spaces'},{cmd:_constants.ELM_COMMANDS.HEADERS_ON,desc:'Enable headers (Required for VIN)'},{cmd:_constants.ELM_COMMANDS.ADAPTIVE_TIMING_1,desc:'Set adaptive timing mode 1'}];for(var _ref of setupCommands){var cmd=_ref.cmd;var desc=_ref.desc;void _logger.log.debug(`[${this.constructor.name}] Setup: ${desc}`);try{var response=yield this.sendCommand(cmd,2000);if(cmd===_constants.ELM_COMMANDS.HEADERS_ON)this.isHeaderEnabled=true;if(response&&!(0,_helpers.isResponseOk)(response)&&!this.isErrorResponse(response)&&response.trim()!=='?'){void _logger.log.warn(`[${this.constructor.name}] Unexpected response for ${cmd}: ${response}`);}else if((response==null?void 0:response.trim())==='?'){void _logger.log.warn(`[${this.constructor.name}] Command "${cmd}" returned '?', possibly unsupported.`);}}catch(error){void _logger.log.error(`[${this.constructor.name}] Error during setup command ${cmd}:`,{error:error instanceof Error?error.message:String(error)});if(cmd===_constants.ELM_COMMANDS.HEADERS_ON){this.protocolState=PROTOCOL_STATES.ERROR;return false;}}yield this.delay(_constants.DELAYS_MS.COMMAND_SHORT);}var protocolDetected=yield this._detectProtocol();if(!protocolDetected){void _logger.log.error(`[${this.constructor.name}] Protocol detection failed.`);this.protocolState=PROTOCOL_STATES.ERROR;return false;}if(this.isCan){yield this._detectEcuResponseHeader();}yield this._configureForProtocol();this.protocolState=PROTOCOL_STATES.READY;void _logger.log.info(`[${this.constructor.name}] Adapter configuration complete. Protocol: ${this.protocolType} (${this.protocolNumber}), isCAN: ${this.isCan}, Headers: ${this.isHeaderEnabled}`,this.ecuResponseHeader?{ecuHeader:this.ecuResponseHeader}:{});return true;});function _configureAdapterForVIN(){return _configureAdapterForVIN2.apply(this,arguments);}return _configureAdapterForVIN;}())},{key:"_detectEcuResponseHeader",value:(function(){var _detectEcuResponseHeader2=(0,_asyncToGenerator2.default)(function*(){if(!this.isCan)return;void _logger.log.debug(`[${this.constructor.name}] Attempting to detect ECU response header (0100)...`);try{var response=yield this._sendCommandWithTiming(_constants.STANDARD_PIDS.SUPPORTED_PIDS_1,5000);if(response&&!this.isErrorResponse(response)){var addresses=(0,_helpers.extractEcuAddresses)(response);if(addresses.length>0&&addresses[0]!==undefined){this.ecuResponseHeader=addresses[0];void _logger.log.info(`[${this.constructor.name}] Detected ECU response header: ${this.ecuResponseHeader}`);}else{void _logger.log.warn(`[${this.constructor.name}] Command 0100 successful, but no valid ECU header extracted from response: ${response}`);}}else{void _logger.log.warn(`[${this.constructor.name}] Failed to get valid response for 0100 header detection: ${response!=null?response:'null'}`);}}catch(error){void _logger.log.error(`[${this.constructor.name}] Error during ECU header detection (0100):`,{error:error instanceof Error?error.message:String(error)});}});function _detectEcuResponseHeader(){return _detectEcuResponseHeader2.apply(this,arguments);}return _detectEcuResponseHeader;}())},{key:"_detectProtocol",value:(function(){var _detectProtocol2=(0,_asyncToGenerator2.default)(function*(){void _logger.log.debug(`[${this.constructor.name}] Detecting protocol (ATDPN)...`);try{var response=yield this.sendCommand(_constants.ELM_COMMANDS.GET_PROTOCOL_NUM,2000);var protocolNum=(0,_helpers.extractProtocolNumber)(response);if(protocolNum===null||this.isErrorResponse(response)){void _logger.log.warn(`[${this.constructor.name}] Failed to get protocol number. Response: ${response!=null?response:'null'}`);this._updateProtocolInfo(-1);return false;}this._updateProtocolInfo(protocolNum);void _logger.log.debug(`[${this.constructor.name}] Protocol detection complete. Number: ${this.protocolNumber}, Type: ${this.protocolType}, isCAN: ${this.isCan}, Header Format: ${this.headerFormat}`);return this.protocolType!==PROTOCOL_TYPES.UNKNOWN;}catch(error){void _logger.log.error(`[${this.constructor.name}] Error detecting protocol:`,{error:error instanceof Error?error.message:String(error)});this._updateProtocolInfo(-1);return false;}});function _detectProtocol(){return _detectProtocol2.apply(this,arguments);}return _detectProtocol;}())},{key:"_updateProtocolInfo",value:function _updateProtocolInfo(protocolNum){this.protocolNumber=protocolNum;if(protocolNum>=6&&protocolNum<=20){this.protocolType=PROTOCOL_TYPES.CAN;this.isCan=true;if(protocolNum===_constants.PROTOCOL.SAE_J1939_CAN_29BIT_250K){this.headerFormat=HEADER_FORMATS.CAN_29BIT;}else if(protocolNum>=_constants.PROTOCOL.ISO_15765_4_CAN_11BIT_500K&&protocolNum<=_constants.PROTOCOL.ISO_15765_4_CAN_29BIT_250K_8){this.headerFormat=protocolNum%2===0?HEADER_FORMATS.CAN_11BIT:HEADER_FORMATS.CAN_29BIT;}else{this.headerFormat=protocolNum%2===0?HEADER_FORMATS.CAN_11BIT:HEADER_FORMATS.CAN_29BIT;}}else if(protocolNum===_constants.PROTOCOL.ISO_9141_2){this.protocolType=PROTOCOL_TYPES.ISO9141;this.headerFormat=HEADER_FORMATS.ISO9141;this.isCan=false;}else if(protocolNum===_constants.PROTOCOL.ISO_14230_4_KWP||protocolNum===_constants.PROTOCOL.ISO_14230_4_KWP_FAST){this.protocolType=PROTOCOL_TYPES.KWP;this.headerFormat=HEADER_FORMATS.KWP;this.isCan=false;}else if(protocolNum===_constants.PROTOCOL.SAE_J1850_PWM||protocolNum===_constants.PROTOCOL.SAE_J1850_VPW){this.protocolType=PROTOCOL_TYPES.J1850;this.headerFormat=HEADER_FORMATS.J1850;this.isCan=false;}else{this.protocolType=PROTOCOL_TYPES.UNKNOWN;this.headerFormat=HEADER_FORMATS.UNKNOWN;this.isCan=false;if(protocolNum<0)this.protocolNumber=_constants.PROTOCOL.AUTO;}}},{key:"_configureForProtocol",value:(function(){var _configureForProtocol2=(0,_asyncToGenerator2.default)(function*(){void _logger.log.debug(`[${this.constructor.name}] Applying config for protocol: ${this.protocolType}`);if(this.isCan){var _this$ecuResponseHead;try{void _logger.log.debug(`[${this.constructor.name}] Enabling CAN Auto Formatting (ATCAF1)`);yield this.sendCommand(_constants.ELM_COMMANDS.CAN_AUTO_FORMAT_ON,2000);}catch(error){void _logger.log.warn(`[${this.constructor.name}] Failed to enable CAN Auto Formatting`,{error:error instanceof Error?error.message:String(error)});}yield this.delay(_constants.DELAYS_MS.COMMAND_SHORT);var fcHeaderToSet=(_this$ecuResponseHead=this.ecuResponseHeader)!=null?_this$ecuResponseHead:this.headerFormat===HEADER_FORMATS.CAN_11BIT?'7E8':'18DAF110';var flowControlData='300000';var flowControlMode='1';void _logger.log.debug(`[${this.constructor.name}] Setting default CAN flow control: Header=${fcHeaderToSet}, Data=${flowControlData}, Mode=${flowControlMode}`);try{yield this.sendCommand(`ATFCSH${fcHeaderToSet}`,2000);yield this.delay(_constants.DELAYS_MS.COMMAND_SHORT);yield this.sendCommand(`ATFCSD${flowControlData}`,2000);yield this.delay(_constants.DELAYS_MS.COMMAND_SHORT);yield this.sendCommand(`ATFCSM${flowControlMode}`,2000);yield this.delay(_constants.DELAYS_MS.COMMAND_SHORT);}catch(error){void _logger.log.warn(`[${this.constructor.name}] Default CAN flow control setup warning:`,{error:error instanceof Error?error.message:String(error)});}}else if(this.protocolType===PROTOCOL_TYPES.KWP){try{void _logger.log.debug(`[${this.constructor.name}] Setting KWP timing (ATAT2)`);yield this.sendCommand(_constants.ELM_COMMANDS.ADAPTIVE_TIMING_2,2000);yield this.delay(_constants.DELAYS_MS.COMMAND_SHORT);}catch(error){void _logger.log.warn(`[${this.constructor.name}] KWP timing (ATAT2) warning:`,{error:error instanceof Error?error.message:String(error)});}}});function _configureForProtocol(){return _configureForProtocol2.apply(this,arguments);}return _configureForProtocol;}())},{key:"_sendCommandWithTiming",value:(function(){var _sendCommandWithTiming2=(0,_asyncToGenerator2.default)(function*(command,timeout){var effectiveTimeout=timeout!=null?timeout:VINRetriever.COMMAND_TIMEOUT;if(!this.isCan&&command===this.mode){effectiveTimeout=timeout!=null?timeout:VINRetriever.DATA_TIMEOUT;void _logger.log.debug(`[${this.constructor.name}] Using longer timeout (${effectiveTimeout}ms) for non-CAN VIN request.`);}void _logger.log.debug(`[${this.constructor.name}] Sending command "${command}" with timeout ${effectiveTimeout}ms`);return yield this.sendCommand(command,effectiveTimeout);});function _sendCommandWithTiming(_x,_x2){return _sendCommandWithTiming2.apply(this,arguments);}return _sendCommandWithTiming;}())},{key:"isErrorResponse",value:function isErrorResponse(response){return response===null||(0,_helpers.isResponseError)(response);}},{key:"_tryOptimizeFlowControl",value:(function(){var _tryOptimizeFlowControl2=(0,_asyncToGenerator2.default)(function*(){var _this$ecuResponseHead2,_this=this;if(!this.isCan){return false;}var baseEcuResponseHeader=(_this$ecuResponseHead2=this.ecuResponseHeader)!=null?_this$ecuResponseHead2:this.headerFormat===HEADER_FORMATS.CAN_11BIT?'7E8':'18DAF110';void _logger.log.debug(`[${this.constructor.name}] Optimizing CAN flow control. Target ECU Response Header: ${baseEcuResponseHeader}`);var baseConfigs=[{fcsh:baseEcuResponseHeader,fcsd:'300000',fcsm:'1',desc:`Std ${baseEcuResponseHeader} (BS=0, ST=0, Mode=1)`},{fcsh:baseEcuResponseHeader,fcsd:'300000',fcsm:'0',desc:`Std ${baseEcuResponseHeader} (BS=0, ST=0, Mode=0)`},{fcsh:baseEcuResponseHeader,fcsd:'300008',fcsm:'1',desc:`Std ${baseEcuResponseHeader} (BS=0, ST=8ms, Mode=1)`},{fcsh:baseEcuResponseHeader,fcsd:'300010',fcsm:'1',desc:`Std ${baseEcuResponseHeader} (BS=0, ST=16ms, Mode=1)`},{fcsh:baseEcuResponseHeader,fcsd:'300200',fcsm:'1',desc:`Std ${baseEcuResponseHeader} (BS=2, ST=0, Mode=1)`},{fcsh:baseEcuResponseHeader,fcsd:'300400',fcsm:'1',desc:`Std ${baseEcuResponseHeader} (BS=4, ST=0, Mode=1)`},{fcsh:baseEcuResponseHeader,fcsd:'300800',fcsm:'1',desc:`Std ${baseEcuResponseHeader} (BS=8, ST=0, Mode=1)`},{fcsh:baseEcuResponseHeader,fcsd:'300204',fcsm:'1',desc:`Std ${baseEcuResponseHeader} (BS=2, ST=4ms, Mode=1)`},{fcsh:baseEcuResponseHeader,fcsd:'300408',fcsm:'1',desc:`Std ${baseEcuResponseHeader} (BS=4, ST=8ms, Mode=1)`},{fcsh:baseEcuResponseHeader,fcsd:'300810',fcsm:'1',desc:`Std ${baseEcuResponseHeader} (BS=8, ST=16ms, Mode=1)`}];if(this.headerFormat===HEADER_FORMATS.CAN_29BIT){var alternateHeaders=['18DAF110','18DAF120','18DAF130'].filter(function(h){return h!==baseEcuResponseHeader;});var separationTimes=['00','04','08','10'];var blockSizes=['00','02','04','08'];for(var header of alternateHeaders){for(var st of separationTimes){for(var bs of blockSizes){if(st==='00'&&bs==='00'||st==='08'&&bs==='04'||st==='10'&&bs==='08'){baseConfigs.push({fcsh:header,fcsd:`30${bs}${st}`,fcsm:'1',desc:`Alt ${header} (BS=${parseInt(bs,16)}, ST=${parseInt(st,16)}ms, Mode=1)`});}}}}}var validateResponse=function validateResponse(response){if(!response||_this.isErrorResponse(response))return false;if(response.includes(_constants.RESPONSE_KEYWORDS.BUFFER_FULL)||response.includes(_constants.RESPONSE_KEYWORDS.FB_ERROR)||response.includes(_constants.RESPONSE_KEYWORDS.CAN_ERROR)){return false;}var cleanResponse=response.replace(/\s/g,'').toUpperCase();if(cleanResponse.length<20)return false;if(!cleanResponse.includes('49'))return false;var dataBytes=cleanResponse.match(/[0-9A-F]{2}/g)||[];if(dataBytes.length<20)return false;return true;};for(var config of baseConfigs){void _logger.log.debug(`[${this.constructor.name}] Trying Flow Control: ${config.desc}`);try{yield this.sendCommand(`ATFCSH${config.fcsh}`,2000);yield this.delay(_constants.DELAYS_MS.COMMAND_SHORT);yield this.sendCommand(`ATFCSD${config.fcsd}`,2000);yield this.delay(_constants.DELAYS_MS.COMMAND_SHORT);yield this.sendCommand(`ATFCSM${config.fcsm}`,2000);yield this.delay(_constants.DELAYS_MS.COMMAND_SHORT);var testResponse=yield this._sendCommandWithTiming(this.mode,VINRetriever.DATA_TIMEOUT);if(validateResponse(testResponse)){void _logger.log.info(`[${this.constructor.name}] Flow control optimization successful with: ${config.desc}`);this.ecuResponseHeader=config.fcsh;return true;}void _logger.log.debug(`[${this.constructor.name}] Config ${config.desc} failed validation`);}catch(error){void _logger.log.warn(`[${this.constructor.name}] Flow control config failed (${config.desc}):`,{error:error instanceof Error?error.message:String(error)});}yield this.delay(_constants.DELAYS_MS.COMMAND_SHORT);}return false;});function _tryOptimizeFlowControl(){return _tryOptimizeFlowControl2.apply(this,arguments);}return _tryOptimizeFlowControl;}())},{key:"_sendVINRequestAndProcess",value:(function(){var _sendVINRequestAndProcess2=(0,_asyncToGenerator2.default)(function*(){try{if(this.protocolState!==PROTOCOL_STATES.READY){void _logger.log.warn(`[${this.constructor.name}] Protocol not ready (State: ${this.protocolState}). Aborting command ${this.mode}.`);this.protocolState=PROTOCOL_STATES.ERROR;return null;}var result=yield this._sendCommandWithTiming(this.mode,VINRetriever.DATA_TIMEOUT);if(result===null||this.isErrorResponse(result)){void _logger.log.warn(`[${this.constructor.name}] Error or no response for command ${this.mode}: ${result!=null?result:'null'}`);if(result!==null&&(result.includes('UNABLE')||result.includes('BUS ERROR')||result.includes('TIMEOUT'))){this.protocolState=PROTOCOL_STATES.ERROR;}return null;}var needsFlowControlCheck=this.isCan&&(result.includes(_constants.RESPONSE_KEYWORDS.BUFFER_FULL)||result.includes(_constants.RESPONSE_KEYWORDS.FB_ERROR)||result.length>0&&result.length<20&&!result.includes(_constants.RESPONSE_KEYWORDS.NO_DATA));if(needsFlowControlCheck){void _logger.log.debug(`[${this.constructor.name}] Detected potential CAN flow control issue. Response: ${result}. Attempting optimization...`);var flowControlSuccess=yield this._tryOptimizeFlowControl();if(flowControlSuccess){void _logger.log.debug(`[${this.constructor.name}] Retrying command ${this.mode} after flow control optimization...`);var retryResult=yield this._sendCommandWithTiming(this.mode,VINRetriever.DATA_TIMEOUT);if(retryResult&&!this.isErrorResponse(retryResult)){void _logger.log.info(`[${this.constructor.name}] Successfully received response after flow control optimization.`);return retryResult;}else{void _logger.log.warn(`[${this.constructor.name}] Command ${this.mode} still failed or gave error after flow control optimization. Response: ${retryResult!=null?retryResult:'null'}. Processing original response.`);}}else{void _logger.log.warn(`[${this.constructor.name}] Flow control optimization failed. Proceeding with original response.`);}}void _logger.log.debug(`[${this.constructor.name}] Processing final response for command ${this.mode}: ${result}`);return result;}catch(error){void _logger.log.error(`[${this.constructor.name}] Error during command execution or response processing for ${this.mode}:`,{error:error instanceof Error?error.message:String(error),stack:error instanceof Error?error.stack:undefined});this.protocolState=PROTOCOL_STATES.ERROR;return null;}});function _sendVINRequestAndProcess(){return _sendVINRequestAndProcess2.apply(this,arguments);}return _sendVINRequestAndProcess;}())},{key:"retrieveVIN",value:(function(){var _retrieveVIN=(0,_asyncToGenerator2.default)(function*(){void _logger.log.debug(`[${this.constructor.name}] Attempting to retrieve VIN...`);var attempt=0;var maxAttempts=3;while(attempt<maxAttempts){attempt++;void _logger.log.debug(`[${this.constructor.name}] VIN Retrieval Attempt ${attempt}/${maxAttempts}`);try{this.resetState();var configSuccess=yield this._configureAdapterForVIN();if(!configSuccess){void _logger.log.error(`[${this.constructor.name}] Adapter configuration failed on attempt ${attempt}.`);if(attempt<maxAttempts)yield this.delay(_constants.DELAYS_MS.RETRY);continue;}var rawResponse=yield this._sendVINRequestAndProcess();if(rawResponse===null){void _logger.log.warn(`[${this.constructor.name}] Failed to retrieve raw response for VIN on attempt ${attempt}.`);if(this.protocolState===PROTOCOL_STATES.ERROR){void _logger.log.error(`[${this.constructor.name}] Protocol entered ERROR state, stopping retries.`);break;}if(attempt<maxAttempts)yield this.delay(_constants.DELAYS_MS.RETRY);continue;}void _logger.log.debug(`[${this.constructor.name}] Raw VIN response received: ${rawResponse}`);var assembledResponse=(0,_helpers.assembleMultiFrameResponse)(rawResponse);void _logger.log.debug(`[${this.constructor.name}] Assembled VIN response data: ${assembledResponse}`);var vin=(0,_helpers.parseVinFromResponse)(assembledResponse);if(vin){var isValidVin=vin.length===17&&/^[A-HJ-NPR-Z0-9]{17}$/i.test(vin);if(isValidVin){void _logger.log.info(`[${this.constructor.name}] Valid VIN found: ${vin}`);return vin;}else{void _logger.log.warn(`[${this.constructor.name}] Invalid VIN format received: ${vin}`);return vin;}}else{void _logger.log.warn(`[${this.constructor.name}] Failed to parse VIN from response on attempt ${attempt}.`);if(attempt<maxAttempts)yield this.delay(_constants.DELAYS_MS.RETRY);}}catch(error){var errorMsg=error instanceof Error?error.message:String(error);void _logger.log.error(`[${this.constructor.name}] Error during VIN retrieval attempt ${attempt}:`,{error:errorMsg,stack:error instanceof Error?error.stack:undefined});this.protocolState=PROTOCOL_STATES.ERROR;if(attempt<maxAttempts){void _logger.log.debug(`[${this.constructor.name}] Retrying after error...`);yield this.delay(_constants.DELAYS_MS.RETRY);}else{break;}}}void _logger.log.error(`[${this.constructor.name}] Failed to retrieve VIN after ${maxAttempts} attempts.`);return null;});function retrieveVIN(){return _retrieveVIN.apply(this,arguments);}return retrieveVIN;}())},{key:"resetState",value:function resetState(){this.isCan=false;this.protocolNumber=_constants.PROTOCOL.AUTO;this.protocolType=PROTOCOL_TYPES.UNKNOWN;this.headerFormat=HEADER_FORMATS.UNKNOWN;this.ecuResponseHeader=null;this.protocolState=PROTOCOL_STATES.INITIALIZED;this.isHeaderEnabled=false;void _logger.log.debug(`[${this.constructor.name}] State reset.`);}},{key:"getServiceMode",value:function getServiceMode(){return VINRetriever.SERVICE_MODE;}}]);}();VINRetriever.SERVICE_MODE={REQUEST:_constants.STANDARD_PIDS.VIN,RESPONSE:0x49,NAME:'VEHICLE_VIN',DESCRIPTION:'Vehicle Identification Number',troubleCodeType:'INFO'};VINRetriever.DATA_TIMEOUT=10000;VINRetriever.COMMAND_TIMEOUT=5000;
//# sourceMappingURL=VINRetriever.js.map