diff --git a/lib/modem-commands.js b/lib/modem-commands.js deleted file mode 100644 index 763201e..0000000 --- a/lib/modem-commands.js +++ /dev/null @@ -1,170 +0,0 @@ -const MUTEX_COMMAND = 'COMMAND'; -exports.MUTEX_COMMAND = MUTEX_COMMAND; - -const MUTEX_SUBCOMMAND = 'SUBCOMMAND'; -exports.MUTEX_SUBCOMMAND = MUTEX_SUBCOMMAND; - -const ParserReadline = require('@serialport/parser-readline'); -const ParserRegex = require('@serialport/parser-regex'); - -const logger = require('komodo-sdk/logger'); -const mutex = require('./mutex-common'); -const parsers = require('./serialport-parsers'); -const modemInfo = require('./modem-info'); - -let port; - -function writeToPort(data) { - return new Promise((resolve) => { - modemInfo.lastWriteTs = new Date(); - port.write(data, (err, bytesWritten) => { - if (err) logger.warn(`ERROR: ${err.toString()}`); - - logger.verbose('OUTGOING', { data: data.toString(), bytesWritten, err }); - resolve(bytesWritten); - }); - }); -} - -exports.writeToPortAndWaitForReadline = function writeToPortAndWaitForReadline(cmd, lockName) { - let resolved = false; - - return new Promise(async (resolve) => { - const parser = new ParserReadline({ delimiter: parsers.PARSER_READLINE_DELIMITER }); - parser.on('data', (data) => { - port.unpipe(parser); - mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim()); - if (!resolved) { - resolved = true; - resolve(data); - } - }); - - await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim()); - port.pipe(parser); - await writeToPort(cmd); - }); -}; - -exports.writeToPortAndWaitForOkOrError = function writeToPortAndWaitForOkOrError(cmd, lockName) { - return new Promise(async (resolve) => { - const parser = new ParserRegex({ regex: /(?:OK|ERROR)\r\n/ }); - parser.on('data', (data) => { - port.unpipe(parser); - mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim()); - resolve(data); - }); - - await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim()); - port.pipe(parser); - await writeToPort(cmd); - }); -}; - -exports.sleep = function sleep(ms) { - return new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, ms || 0); - }); -}; - - -exports.setPort = function setPort(val) { - port = val; -}; - -exports.querySignalQuality = function querySignalQuality() { - return new Promise(async (resolve) => { - if (!mutex.tryLock(MUTEX_COMMAND, 'querySignalQuality')) { - resolve(false); - return; - } - - await writeToPort('AT+CSQ\r'); - mutex.unlock(MUTEX_COMMAND, 'querySignalQuality'); - resolve(true); - }); -}; - -exports.queryCOPS = function queryCOPS(lockName) { - return new Promise(async (resolve) => { - await mutex.lock(lockName || MUTEX_COMMAND, 'queryCOPS'); - await writeToPort('AT+COPS?\r'); - mutex.unlock(lockName || MUTEX_COMMAND, 'queryCOPS'); - resolve(true); - }); -}; - -exports.queryCOPSAndSignalQuality = function queryCOPSAndSignalQuality(skipOnLocked) { - return new Promise(async (resolve) => { - if (!skipOnLocked) { - await mutex.lock(MUTEX_COMMAND); - } else if (!mutex.tryLock(MUTEX_COMMAND, 'queryCOPSAndSignalQuality')) { - resolve(false); - return; - } - - await this.writeToPortAndWaitForOkOrError('AT+COPS?\r', MUTEX_SUBCOMMAND); - await this.writeToPortAndWaitForOkOrError('AT+CSQ\r', MUTEX_SUBCOMMAND); - - mutex.unlock(MUTEX_COMMAND, 'queryCopsAndSignalQuality'); - resolve(true); - }); -}; - -exports.queryIMEI = function queryIMEI(lockName) { - return new Promise(async (resolve) => { - const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); - parser.on('data', (data) => { - logger.verbose('INCOMING', { data: data.toString(), parser: 'parserIMEI' }); - port.unpipe(parser); - mutex.unlock(lockName || MUTEX_COMMAND, 'queryIMEI'); - modemInfo.imei = data.toString().trim() || null; - logger.info('IMEI extracted', { imei: modemInfo.imei }); - resolve(modemInfo.imei); - }); - - await mutex.lock(lockName || MUTEX_COMMAND, 'queryIMEI'); - - port.pipe(parser); - await writeToPort('AT+CGSN\r'); - }); -}; - -exports.queryIMSI = function queryIMSI(lockName) { - return new Promise(async (resolve) => { - const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); - parser.on('data', (data) => { - logger.verbose('INCOMING', { data: data.toString(), parser: 'parserIMSI' }); - port.unpipe(parser); - mutex.unlock(lockName || MUTEX_COMMAND, 'queryIMSI'); - modemInfo.imsi = data.toString().trim() || null; - logger.info('IMSI extracted', { imsi: modemInfo.imsi }); - resolve(modemInfo.imsi); - }); - - await mutex.lock(lockName || MUTEX_COMMAND, 'queryIMSI'); - - port.pipe(parser); - await writeToPort('AT+CIMI\r'); - }); -}; - -exports.queryIMEIAndIMSI = async function queryIMEIAndIMSI() { - await mutex.lock(MUTEX_COMMAND, 'queryIMEIAndIMSI'); - - const imei = await this.queryIMEI(MUTEX_SUBCOMMAND); - const imsi = await this.queryIMSI(MUTEX_SUBCOMMAND); - - await mutex.unlock(MUTEX_COMMAND, 'queryIMEIAndIMSI'); - return { imei, imsi }; -}; - -exports.initATCommands = async function initATCommands() { - await mutex.lock(MUTEX_COMMAND, 'INIT MODEM'); - await this.writeToPortAndWaitForOkOrError('ATE0\r', MUTEX_SUBCOMMAND); - await this.writeToPortAndWaitForOkOrError('AT+CMGF=0\r', MUTEX_SUBCOMMAND); - await this.writeToPortAndWaitForOkOrError('AT+CNMI=1,2,0,1,0\r', MUTEX_SUBCOMMAND); - mutex.unlock(MUTEX_COMMAND, 'INIT MODEM'); -}; diff --git a/lib/modem-commands/index.js b/lib/modem-commands/index.js new file mode 100644 index 0000000..2d15a77 --- /dev/null +++ b/lib/modem-commands/index.js @@ -0,0 +1,216 @@ +const MUTEX_COMMAND = 'COMMAND'; +exports.MUTEX_COMMAND = MUTEX_COMMAND; + +const MUTEX_SUBCOMMAND = 'SUBCOMMAND'; +exports.MUTEX_SUBCOMMAND = MUTEX_SUBCOMMAND; + +const pdu = require('node-pdu'); + +const ParserReadline = require('@serialport/parser-readline'); +const ParserRegex = require('@serialport/parser-regex'); + +const logger = require('komodo-sdk/logger'); +const mutex = require('../mutex-common'); +const parsers = require('../serialport-parsers'); +const modemInfo = require('../modem-info'); + +let port; + +function writeToPort(data) { + return new Promise((resolve) => { + modemInfo.lastWriteTs = new Date(); + port.write(data, (err, bytesWritten) => { + if (err) logger.warn(`ERROR: ${err.toString()}`); + + logger.verbose('OUTGOING', { data: data.toString(), bytesWritten, err }); + resolve(bytesWritten); + }); + }); +} + +exports.writeToPortAndWaitForReadline = function writeToPortAndWaitForReadline(cmd, lockName) { + let resolved = false; + + return new Promise(async (resolve) => { + const parser = new ParserReadline({ delimiter: parsers.PARSER_READLINE_DELIMITER }); + parser.on('data', (data) => { + port.unpipe(parser); + mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim()); + if (!resolved) { + resolved = true; + resolve(data); + } + }); + + await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim()); + port.pipe(parser); + await writeToPort(cmd); + }); +}; + +exports.writeToPortAndWaitForOkOrError = function writeToPortAndWaitForOkOrError(cmd, lockName) { + return new Promise(async (resolve) => { + const parser = new ParserRegex({ regex: /(?:OK|ERROR)\r\n/ }); + parser.on('data', (data) => { + port.unpipe(parser); + mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim()); + resolve(data); + }); + + await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim()); + port.pipe(parser); + await writeToPort(cmd); + }); +}; + +exports.sleep = function sleep(ms) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, ms || 0); + }); +}; + + +exports.setPort = function setPort(val) { + port = val; +}; + +exports.querySignalQuality = function querySignalQuality() { + return new Promise(async (resolve) => { + if (!mutex.tryLock(MUTEX_COMMAND, 'querySignalQuality')) { + resolve(false); + return; + } + + await writeToPort('AT+CSQ\r'); + mutex.unlock(MUTEX_COMMAND, 'querySignalQuality'); + resolve(true); + }); +}; + +exports.queryCOPS = function queryCOPS(lockName) { + return new Promise(async (resolve) => { + await mutex.lock(lockName || MUTEX_COMMAND, 'queryCOPS'); + await writeToPort('AT+COPS?\r'); + mutex.unlock(lockName || MUTEX_COMMAND, 'queryCOPS'); + resolve(true); + }); +}; + +exports.queryCOPSAndSignalQuality = function queryCOPSAndSignalQuality(skipOnLocked) { + return new Promise(async (resolve) => { + if (!skipOnLocked) { + await mutex.lock(MUTEX_COMMAND); + } else if (!mutex.tryLock(MUTEX_COMMAND, 'queryCOPSAndSignalQuality')) { + resolve(false); + return; + } + + await this.writeToPortAndWaitForOkOrError('AT+COPS?\r', MUTEX_SUBCOMMAND); + await this.writeToPortAndWaitForOkOrError('AT+CSQ\r', MUTEX_SUBCOMMAND); + + mutex.unlock(MUTEX_COMMAND, 'queryCopsAndSignalQuality'); + resolve(true); + }); +}; + +exports.queryIMEI = function queryIMEI(lockName) { + return new Promise(async (resolve) => { + const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); + parser.on('data', (data) => { + logger.verbose('INCOMING', { data: data.toString(), parser: 'parserIMEI' }); + port.unpipe(parser); + mutex.unlock(lockName || MUTEX_COMMAND, 'queryIMEI'); + modemInfo.imei = data.toString().trim() || null; + logger.info('IMEI extracted', { imei: modemInfo.imei }); + resolve(modemInfo.imei); + }); + + await mutex.lock(lockName || MUTEX_COMMAND, 'queryIMEI'); + + port.pipe(parser); + await writeToPort('AT+CGSN\r'); + }); +}; + +exports.queryIMSI = function queryIMSI(lockName) { + return new Promise(async (resolve) => { + const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); + parser.on('data', (data) => { + logger.verbose('INCOMING', { data: data.toString(), parser: 'parserIMSI' }); + port.unpipe(parser); + mutex.unlock(lockName || MUTEX_COMMAND, 'queryIMSI'); + modemInfo.imsi = data.toString().trim() || null; + logger.info('IMSI extracted', { imsi: modemInfo.imsi }); + resolve(modemInfo.imsi); + }); + + await mutex.lock(lockName || MUTEX_COMMAND, 'queryIMSI'); + + port.pipe(parser); + await writeToPort('AT+CIMI\r'); + }); +}; + +exports.queryIMEIAndIMSI = async function queryIMEIAndIMSI() { + await mutex.lock(MUTEX_COMMAND, 'queryIMEIAndIMSI'); + + const imei = await this.queryIMEI(MUTEX_SUBCOMMAND); + const imsi = await this.queryIMSI(MUTEX_SUBCOMMAND); + + await mutex.unlock(MUTEX_COMMAND, 'queryIMEIAndIMSI'); + return { imei, imsi }; +}; + +exports.queryManufacturer = function queryManufacturer(lockName) { + return new Promise(async (resolve) => { + const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); + parser.on('data', (data) => { + logger.verbose('INCOMING', { data: data.toString(), parser: 'parserManufacturer' }); + port.unpipe(parser); + mutex.unlock(lockName || MUTEX_COMMAND, 'parserManufacturer'); + modemInfo.manufacturer = data.toString().trim() || null; + logger.info('Manufacturer extracted', { manufacturer: modemInfo.manufacturer }); + resolve(modemInfo.manufacturer); + }); + + await mutex.lock(lockName || MUTEX_COMMAND, 'queryManufacturer'); + + port.pipe(parser); + await writeToPort('AT+CGMI\r'); + }); +}; + +exports.queryModel = function queryModel(lockName) { + return new Promise(async (resolve) => { + const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); + parser.on('data', (data) => { + logger.verbose('INCOMING', { data: data.toString(), parser: 'parserModel' }); + port.unpipe(parser); + mutex.unlock(lockName || MUTEX_COMMAND, 'parserModel'); + modemInfo.model = data.toString().trim() || null; + logger.info('Model extracted', { model: modemInfo.model }); + resolve(modemInfo.model); + }); + + await mutex.lock(lockName || MUTEX_COMMAND, 'queryModel'); + + port.pipe(parser); + await writeToPort('AT+CGMM\r'); + }); +}; + +exports.sendSMS = function sendSMS(destination, msg) { + return new Promise(async (resolve) => { + + }); +}; + +exports.initATCommands = async function initATCommands() { + await mutex.lock(MUTEX_COMMAND, 'INIT MODEM'); + await this.writeToPortAndWaitForOkOrError('\rATE0\r', MUTEX_SUBCOMMAND); + await this.writeToPortAndWaitForOkOrError('AT+CMGF=0\r', MUTEX_SUBCOMMAND); + await this.writeToPortAndWaitForOkOrError('AT+CNMI=1,2,0,1,0\r', MUTEX_SUBCOMMAND); + mutex.unlock(MUTEX_COMMAND, 'INIT MODEM'); +}; diff --git a/modem-tester.js b/modem-tester.js index df61a89..87b0b36 100644 --- a/modem-tester.js +++ b/modem-tester.js @@ -20,23 +20,29 @@ const port = new SerialPort(config.modem.device, { baudRate: 115200 }, async (er process.exit(1); } - await modemCommands.writeToPortAndWaitForOkOrError('AT\r'); + await modemCommands.writeToPortAndWaitForOkOrError('\rAT\r'); await modemCommands.writeToPortAndWaitForOkOrError('AT&FE0\r'); await modemCommands.writeToPortAndWaitForOkOrError('AT+CMGF=0\r'); await modemCommands.writeToPortAndWaitForOkOrError('AT+CNMI=1,2,0,1,0\r'); + await modemCommands.queryManufacturer(); + await modemCommands.queryModel(); + await modemCommands.queryIMEIAndIMSI(); await modemCommands.queryCOPSAndSignalQuality(); logger.info('Modem state', modemInfo); setInterval(async () => { await modemCommands.initATCommands(); + await modemCommands.queryManufacturer(); + await modemCommands.queryModel(); await modemCommands.queryIMEIAndIMSI(); await modemCommands.queryCOPSAndSignalQuality(); logger.info('Modem state', modemInfo); }, (config && config.interval_beetwen_signal_strength_ms) || 30000); }); +global.MODEM_PORT = port; parsers.setPort(port); modemCommands.setPort(port);