From 685cb56836b25a51796c1dd17f6b1807460dd27b Mon Sep 17 00:00:00 2001 From: Hugo Montero Date: Thu, 9 May 2024 12:11:17 -0600 Subject: [PATCH 1/2] use serial identify in case the device does not support control request --- src/cmd/serial.js | 122 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 5 deletions(-) diff --git a/src/cmd/serial.js b/src/cmd/serial.js index f182650bf..df289eaae 100644 --- a/src/cmd/serial.js +++ b/src/cmd/serial.js @@ -21,6 +21,8 @@ const usbUtils = require('./usb-util'); const { platformForId } = require('../lib/platform'); const { FirmwareModuleDisplayNames } = require('particle-usb'); +const IDENTIFY_COMMAND_TIMEOUT = 20000; + // TODO: DRY this up somehow // The categories of output will be handled via the log class, and similar for protip. @@ -259,18 +261,38 @@ module.exports = class SerialCommand extends CLICommandBase { // Obtain system firmware version fwVer = device.firmwareVersion; + // If the device is a cellular device, obtain imei and iccid - try { - const features = platformForId(device.platformId).features; - if (features.includes('cellular')) { + + const features = platformForId(device.platformId).features; + if (features.includes('cellular')) { + let isControlRequestSupported = true; + try { const cellularMetrics = await device.getCellularInfo(); cellularImei = cellularMetrics.imei; cellularIccid = cellularMetrics.iccid; + } catch (err) { + // ignore and move on to get other fields + if (err.message === 'Not supported') { + isControlRequestSupported = false; + } + } + if (!isControlRequestSupported) { + try { + const cellularInfo = await this.getDeviceInfoFromSerial(deviceFromSerialPort); + if (!cellularInfo) { + throw new VError('No data returned from serial port for device info'); + } + cellularImei = cellularInfo.imei; + cellularIccid = cellularInfo.iccid; + } catch (err) { + // ignore and move on to get other fields + throw new VError(ensureError(err), 'Could not get device info, ensure the device is in listening mode'); + } } - } catch (err) { - // ignore and move on to get other fields } + // Print whatever was obtained from the device this._printIdentifyInfo({ deviceId, @@ -1087,5 +1109,95 @@ module.exports = class SerialCommand extends CLICommandBase { ); process.exit(0); } + + getDeviceInfoFromSerial(device){ + return this._issueSerialCommand(device, 'i', IDENTIFY_COMMAND_TIMEOUT) + .then((data) => { + const matches = data.match(/Your (core|device) id is\s+(\w+)/); + + if (matches && matches.length === 3){ + return matches[2]; + } + + const electronMatches = data.match(/\s+([a-fA-F0-9]{24})\s+/); + + if (electronMatches && electronMatches.length === 2){ + const info = { id: electronMatches[1] }; + const imeiMatches = data.match(/IMEI: (\w+)/); + + if (imeiMatches){ + info.imei = imeiMatches[1]; + } + + const iccidMatches = data.match(/ICCID: (\w+)/); + + if (iccidMatches){ + info.iccid = iccidMatches[1]; + } + + return info; + } + }); + } + + /* eslint-enable max-statements */ + + /** + * Sends a command to the device and retrieves the response. + * @param devicePort The device port to send the command to + * @param command The command text + * @param timeout How long in milliseconds to wait for a response + * @returns {Promise} to send the command. + * The serial port should not be open, and is closed after the command is sent. + * @private + */ + _issueSerialCommand(device, command, timeout){ + if (!device){ + throw new VError('No serial port identified'); + } + const failDelay = timeout || 5000; + + let serialPort; + return new Promise((resolve, reject) => { + serialPort = new SerialPort({ path: device.port, ...SERIAL_PORT_DEFAULTS }); + const parser = new SerialBatchParser({ timeout: 250 }); + serialPort.pipe(parser); + + const failTimer = setTimeout(() => { + reject(timeoutError); + }, failDelay); + + parser.on('data', (data) => { + clearTimeout(failTimer); + resolve(data.toString()); + }); + + serialPort.open((err) => { + if (err){ + console.error('Serial err: ' + err); + console.error('Serial problems, please reconnect the device.'); + reject('Serial problems, please reconnect the device.'); + return; + } + + serialPort.write(command, (werr) => { + if (werr){ + reject(err); + } + }); + }); + }) + .finally(() => { + if (serialPort){ + serialPort.removeAllListeners('open'); + + if (serialPort.isOpen){ + return new Promise((resolve) => { + serialPort.close(resolve); + }); + } + } + }); + } }; From f15351562b10d29b4f6a843e9545ad285e2a2f05 Mon Sep 17 00:00:00 2001 From: Hugo Montero Date: Thu, 9 May 2024 12:49:33 -0600 Subject: [PATCH 2/2] use fwVersion to choose which command use --- src/cmd/serial.js | 25 ++++++++++++++----------- src/cmd/serial.test.js | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/cmd/serial.js b/src/cmd/serial.js index df289eaae..86d6315b8 100644 --- a/src/cmd/serial.js +++ b/src/cmd/serial.js @@ -20,6 +20,7 @@ const FlashCommand = require('./flash'); const usbUtils = require('./usb-util'); const { platformForId } = require('../lib/platform'); const { FirmwareModuleDisplayNames } = require('particle-usb'); +const semver = require('semver'); const IDENTIFY_COMMAND_TIMEOUT = 20000; @@ -266,18 +267,20 @@ module.exports = class SerialCommand extends CLICommandBase { const features = platformForId(device.platformId).features; if (features.includes('cellular')) { - let isControlRequestSupported = true; - try { - const cellularMetrics = await device.getCellularInfo(); - cellularImei = cellularMetrics.imei; - cellularIccid = cellularMetrics.iccid; - } catch (err) { - // ignore and move on to get other fields - if (err.message === 'Not supported') { - isControlRequestSupported = false; + // since from 6.x onwards we can't use serial to get imei, we use control request + if (semver.gte(fwVer, '6.0.0')) { + try { + const cellularInfo = await device.getCellularInfo({ timeout: 2000 }); + if (!cellularInfo) { + throw new VError('No data returned from control request for device info'); + } + cellularImei = cellularInfo.imei; + cellularIccid = cellularInfo.iccid; + } catch (err) { + // ignore and move on to get other fields + throw new VError(ensureError(err), 'Could not get device info'); } - } - if (!isControlRequestSupported) { + } else { try { const cellularInfo = await this.getDeviceInfoFromSerial(deviceFromSerialPort); if (!cellularInfo) { diff --git a/src/cmd/serial.test.js b/src/cmd/serial.test.js index 2904efa7d..da7c9e56b 100644 --- a/src/cmd/serial.test.js +++ b/src/cmd/serial.test.js @@ -25,7 +25,7 @@ describe('Serial Command', () => { describe('identifyDevice', () => { it('identifies a cellular device with dvos over serial', async () => { const deviceId = '1234456789abcdef'; - const fwVer = '5.4.0'; + const fwVer = '6.1.0'; const imei = '1234'; const iccid = '5678'; const wifiDeviceFromSerialPort = {