Commit e77dfd454654c8ae786e7ba0e7981601a7ee35b1
1 parent
d54ac95033
Exists in
master
queryIMEI
Showing 3 changed files with 24 additions and 2 deletions Inline Diff
lib/modem-commands.js
1 | const MUTEX_COMMAND = 'COMMAND'; | 1 | const MUTEX_COMMAND = 'COMMAND'; |
2 | // const MUTEX_SUBCOMMAND = 'SUBCOMMAND'; | 2 | // const MUTEX_SUBCOMMAND = 'SUBCOMMAND'; |
3 | 3 | ||
4 | const ParserRegex = require('@serialport/parser-regex'); | ||
5 | |||
4 | const logger = require('komodo-sdk/logger'); | 6 | const logger = require('komodo-sdk/logger'); |
5 | const mutex = require('./mutex-common'); | 7 | const mutex = require('./mutex-common'); |
6 | // const parsers = require('./serialport-parsers'); | 8 | const parsers = require('./serialport-parsers'); |
7 | 9 | ||
8 | let port; | 10 | let port; |
9 | 11 | ||
10 | function writeToPort(data) { | 12 | function writeToPort(data) { |
11 | return new Promise((resolve) => { | 13 | return new Promise((resolve) => { |
12 | port.write(data, (err, bytesWritten) => { | 14 | port.write(data, (err, bytesWritten) => { |
13 | if (err) logger.warn(`ERROR: ${err.toString()}`); | 15 | if (err) logger.warn(`ERROR: ${err.toString()}`); |
14 | 16 | ||
15 | logger.verbose('OUTGOING', { bytesWritten, data: data.toString() }); | 17 | logger.verbose('OUTGOING', { bytesWritten, data: data.toString() }); |
16 | resolve(bytesWritten); | 18 | resolve(bytesWritten); |
17 | }); | 19 | }); |
18 | }); | 20 | }); |
19 | } | 21 | } |
20 | 22 | ||
21 | exports.sleep = function sleep(ms) { | 23 | exports.sleep = function sleep(ms) { |
22 | return new Promise((resolve) => { | 24 | return new Promise((resolve) => { |
23 | setTimeout(() => { | 25 | setTimeout(() => { |
24 | resolve(); | 26 | resolve(); |
25 | }, ms || 0); | 27 | }, ms || 0); |
26 | }); | 28 | }); |
27 | }; | 29 | }; |
28 | 30 | ||
29 | 31 | ||
30 | exports.setPort = function setPort(val) { | 32 | exports.setPort = function setPort(val) { |
31 | port = val; | 33 | port = val; |
32 | }; | 34 | }; |
33 | 35 | ||
34 | exports.querySignalQuality = function querySignalQuality() { | 36 | exports.querySignalQuality = function querySignalQuality() { |
35 | return new Promise(async (resolve) => { | 37 | return new Promise(async (resolve) => { |
36 | if (!mutex.tryLock(MUTEX_COMMAND, 'querySignalQuality')) { | 38 | if (!mutex.tryLock(MUTEX_COMMAND, 'querySignalQuality')) { |
37 | resolve(false); | 39 | resolve(false); |
38 | return; | 40 | return; |
39 | } | 41 | } |
40 | 42 | ||
41 | await writeToPort('AT+CSQ\r'); | 43 | await writeToPort('AT+CSQ\r'); |
42 | mutex.unlock(MUTEX_COMMAND, 'querySignalQuality'); | 44 | mutex.unlock(MUTEX_COMMAND, 'querySignalQuality'); |
43 | resolve(true); | 45 | resolve(true); |
44 | }); | 46 | }); |
45 | }; | 47 | }; |
46 | 48 | ||
47 | exports.queryCOPS = function querySignalQuality() { | 49 | exports.queryCOPS = function querySignalQuality() { |
48 | return new Promise(async (resolve) => { | 50 | return new Promise(async (resolve) => { |
49 | await mutex.lock(MUTEX_COMMAND, 'queryCOPS'); | 51 | await mutex.lock(MUTEX_COMMAND, 'queryCOPS'); |
50 | await writeToPort('AT+COPS?\r'); | 52 | await writeToPort('AT+COPS?\r'); |
51 | mutex.unlock(MUTEX_COMMAND, 'queryCOPS'); | 53 | mutex.unlock(MUTEX_COMMAND, 'queryCOPS'); |
52 | resolve(true); | 54 | resolve(true); |
53 | }); | 55 | }); |
54 | }; | 56 | }; |
57 | |||
58 | exports.queryIMEI = function queryIMEI() { | ||
59 | return new Promise(async (resolve) => { | ||
60 | const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); | ||
61 | parser.on('data', (data) => { | ||
62 | logger.verbose('INCOMING', { parser: 'parserIMEI', data: data.toString() }); | ||
63 | mutex.unlock(MUTEX_COMMAND, 'queryIMEI'); | ||
64 | resolve(); | ||
65 | }); | ||
66 | |||
67 | await mutex.lock(MUTEX_COMMAND, 'queryIMEI'); | ||
68 | |||
69 | port.pipe(parser); | ||
70 | await writeToPort('AT+CGSN\r'); | ||
71 | port.unpipe(parser); | ||
72 | }); | ||
73 | }; | ||
55 | 74 |
lib/serialport-parsers.js
1 | const PARSER_READLINE_DELIMITER = '\r\n'; | 1 | const PARSER_READLINE_DELIMITER = '\r\n'; |
2 | const PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = /\r\n(?:OK|ERROR)\r\n/; | 2 | const PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = /\r\n(?:OK|ERROR)\r\n/; |
3 | 3 | ||
4 | const pdu = require('node-pdu'); | 4 | const pdu = require('node-pdu'); |
5 | const ParserReadline = require('@serialport/parser-readline'); | 5 | const ParserReadline = require('@serialport/parser-readline'); |
6 | const ParserRegex = require('@serialport/parser-regex'); | 6 | const ParserRegex = require('@serialport/parser-regex'); |
7 | 7 | ||
8 | const logger = require('komodo-sdk/logger'); | 8 | const logger = require('komodo-sdk/logger'); |
9 | 9 | ||
10 | const dbCops = require('./db-cops'); | 10 | const dbCops = require('./db-cops'); |
11 | 11 | ||
12 | let port; | 12 | let port; |
13 | 13 | ||
14 | exports.setPort = function setPort(val) { | 14 | exports.setPort = function setPort(val) { |
15 | logger.info('SERIALPORT-PARSERS: setting port'); | 15 | logger.info('SERIALPORT-PARSERS: setting port'); |
16 | port = val; | 16 | port = val; |
17 | }; | 17 | }; |
18 | 18 | ||
19 | exports.getPort = function getPort() { | 19 | exports.getPort = function getPort() { |
20 | return port; | 20 | return port; |
21 | }; | 21 | }; |
22 | 22 | ||
23 | function parsePdu(data) { | 23 | function parsePdu(data) { |
24 | if (!data) return null; | 24 | if (!data) return null; |
25 | 25 | ||
26 | try { | 26 | try { |
27 | const result = pdu.parse(data.toString().trim() || ''); | 27 | const result = pdu.parse(data.toString().trim() || ''); |
28 | return result; | 28 | return result; |
29 | } catch (e) { | 29 | } catch (e) { |
30 | return null; | 30 | return null; |
31 | } | 31 | } |
32 | } | 32 | } |
33 | 33 | ||
34 | function onCSQ(data) { | 34 | function onCSQ(data) { |
35 | const val = data.toString().trim().match(/\+CSQ:\s*(.*)/); | 35 | const val = data.toString().trim().match(/\+CSQ:\s*(.*)/); |
36 | if (!val || !val[1]) return null; | 36 | if (!val || !val[1]) return null; |
37 | 37 | ||
38 | logger.info('Signal quality extracted', { signalQuality: val[1] }); | 38 | logger.info('Signal quality extracted', { signalQuality: val[1] }); |
39 | return val[1]; | 39 | return val[1]; |
40 | } | 40 | } |
41 | 41 | ||
42 | function onPduDeliver(data, parsedData) { | 42 | function onPduDeliver(data, parsedData) { |
43 | const from = parsedData.getAddress && parsedData.getAddress().getPhone | 43 | const from = parsedData.getAddress && parsedData.getAddress().getPhone |
44 | ? parsedData.getAddress().getPhone() : null; | 44 | ? parsedData.getAddress().getPhone() : null; |
45 | 45 | ||
46 | const msg = parsedData.getData && parsedData.getData().getData | 46 | const msg = parsedData.getData && parsedData.getData().getData |
47 | ? parsedData.getData().getData() : null; | 47 | ? parsedData.getData().getData() : null; |
48 | 48 | ||
49 | logger.verbose('PDU processed', { from, msg }); | 49 | logger.verbose('PDU processed', { from, msg }); |
50 | return { from, msg }; | 50 | return { from, msg }; |
51 | } | 51 | } |
52 | 52 | ||
53 | function onCOPS(data) { | 53 | function onCOPS(data) { |
54 | const val = data.toString().trim().match(/\+COPS:\s*(.*)/); | 54 | const val = data.toString().trim().match(/\+COPS:\s*(.*)/); |
55 | if (!val || !val[1]) return null; | 55 | if (!val || !val[1]) return null; |
56 | 56 | ||
57 | const cops = val[1]; | 57 | const cops = val[1]; |
58 | 58 | ||
59 | if (!cops) return null; | 59 | if (!cops) return null; |
60 | const [mode, format, networkId] = cops.split(','); | 60 | const [mode, format, networkId] = cops.split(','); |
61 | const networkName = networkId ? dbCops[networkId] || networkId : null; | 61 | const networkName = networkId ? dbCops[networkId] || networkId : null; |
62 | 62 | ||
63 | logger.verbose('COPS extracted', { | 63 | logger.verbose('COPS extracted', { |
64 | cops, mode, format, networkId, networkName, | 64 | cops, mode, format, networkId, networkName, |
65 | }); | 65 | }); |
66 | 66 | ||
67 | return { | 67 | return { |
68 | cops, mode, format, networkId, networkName, | 68 | cops, mode, format, networkId, networkName, |
69 | }; | 69 | }; |
70 | } | 70 | } |
71 | 71 | ||
72 | 72 | ||
73 | function isResultCodeIs(data, resultCode) { | 73 | function isResultCodeIs(data, resultCode) { |
74 | if (!data) return false; | 74 | if (!data) return false; |
75 | const cleanedData = (data.toString() || '').trim(); | 75 | const cleanedData = (data.toString() || '').trim(); |
76 | if (!cleanedData) return false; | 76 | if (!cleanedData) return false; |
77 | 77 | ||
78 | if (resultCode.indexOf('+') !== 0) { | 78 | if (resultCode.indexOf('+') !== 0) { |
79 | // eslint-disable-next-line no-param-reassign | 79 | // eslint-disable-next-line no-param-reassign |
80 | resultCode = `+${resultCode}`; | 80 | resultCode = `+${resultCode}`; |
81 | } | 81 | } |
82 | 82 | ||
83 | if (resultCode.search(/:$/) < 0) { | 83 | if (resultCode.search(/:$/) < 0) { |
84 | // eslint-disable-next-line no-param-reassign | 84 | // eslint-disable-next-line no-param-reassign |
85 | resultCode += ':'; | 85 | resultCode += ':'; |
86 | } | 86 | } |
87 | 87 | ||
88 | return cleanedData.indexOf(resultCode) === 0; | 88 | return cleanedData.indexOf(resultCode) === 0; |
89 | } | 89 | } |
90 | 90 | ||
91 | const parserReadline = new ParserReadline({ delimiter: PARSER_READLINE_DELIMITER }); | 91 | const parserReadline = new ParserReadline({ delimiter: PARSER_READLINE_DELIMITER }); |
92 | parserReadline.on('data', (data) => { | 92 | parserReadline.on('data', (data) => { |
93 | logger.verbose('INCOMING', { parser: 'parserReadLine', data: `${data.toString()}${PARSER_READLINE_DELIMITER}` }); | 93 | logger.verbose('INCOMING', { parser: 'parserReadLine', data: `${data.toString()}${PARSER_READLINE_DELIMITER}` }); |
94 | 94 | ||
95 | if (!data) return; | 95 | if (!data) return; |
96 | 96 | ||
97 | const pduParsed = parsePdu(data); | 97 | const pduParsed = parsePdu(data); |
98 | if (pduParsed) { | 98 | if (pduParsed) { |
99 | logger.verbose('PDU parsed', { type: (typeof pduParsed.getType === 'function') && pduParsed.getType() }); | 99 | logger.verbose('PDU parsed', { type: (typeof pduParsed.getType === 'function') && pduParsed.getType() }); |
100 | } | 100 | } |
101 | 101 | ||
102 | if (pduParsed && pduParsed.constructor.name !== 'Deliver') { | 102 | if (pduParsed && pduParsed.constructor.name !== 'Deliver') { |
103 | logger.warn('Unknown PDU message type name. PLEASE REPORT IT TO DEVELOPER AT TEKTRANS', { typeName: pduParsed.constructor.name, type: pduParsed.getType(), data: data.toString().trim() }); | 103 | logger.warn('Unknown PDU message type name. PLEASE REPORT IT TO DEVELOPER AT TEKTRANS', { typeName: pduParsed.constructor.name, type: pduParsed.getType(), data: data.toString().trim() }); |
104 | } | 104 | } |
105 | 105 | ||
106 | if (pduParsed && pduParsed.constructor.name === 'Deliver' && pduParsed.getType && pduParsed.getType().getSrr()) { | 106 | if (pduParsed && pduParsed.constructor.name === 'Deliver' && pduParsed.getType && pduParsed.getType().getSrr()) { |
107 | logger.verbose('Got a PDU SMS-DELIVER', { type: pduParsed.getType() }); | 107 | logger.verbose('Got a PDU SMS-DELIVER', { type: pduParsed.getType() }); |
108 | onPduDeliver(data, pduParsed); | 108 | onPduDeliver(data, pduParsed); |
109 | } else if (isResultCodeIs(data, 'CSQ')) { | 109 | } else if (isResultCodeIs(data, 'CSQ')) { |
110 | logger.verbose('Got a signal quality report', { data: data.toString() }); | 110 | logger.verbose('Got a signal quality report', { data: data.toString() }); |
111 | onCSQ(data); | 111 | onCSQ(data); |
112 | } else if (isResultCodeIs(data, 'COPS:')) { | 112 | } else if (isResultCodeIs(data, 'COPS:')) { |
113 | logger.verbose('Got a COPS report', { data: data.toString() }); | 113 | logger.verbose('Got a COPS report', { data: data.toString() }); |
114 | onCOPS(data); | 114 | onCOPS(data); |
115 | } else if (isResultCodeIs(data, 'CMT')) { | 115 | } else if (isResultCodeIs(data, 'CMT')) { |
116 | logger.verbose('Got a new message report', { data: data.toString() }); | 116 | logger.verbose('Got a new message report', { data: data.toString() }); |
117 | } else if (isResultCodeIs(data, 'CMTI')) { | 117 | } else if (isResultCodeIs(data, 'CMTI')) { |
118 | logger.verbose('Got a new message notification report', { data: data.toString() }); | 118 | logger.verbose('Got a new message notification report', { data: data.toString() }); |
119 | } | 119 | } |
120 | }); | 120 | }); |
121 | 121 | ||
122 | const parserWaitForOkOrError = new ParserRegex({ regex: PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); | 122 | const parserWaitForOkOrError = new ParserRegex({ regex: PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); |
123 | parserWaitForOkOrError.on('data', (data) => { | 123 | parserWaitForOkOrError.on('data', (data) => { |
124 | logger.verbose('INCOMING', { parser: 'parserWaitForOkOrError', data: data.toString() }); | 124 | logger.verbose('INCOMING', { parser: 'parserWaitForOkOrError', data: data.toString() }); |
125 | }); | 125 | }); |
126 | 126 | ||
127 | 127 | ||
128 | exports.PARSER_READLINE_DELIMITER = PARSER_READLINE_DELIMITER; | ||
129 | exports.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = PARSER_WAIT_FOR_OK_OR_ERROR_REGEX; | ||
130 | |||
128 | exports.parserReadline = parserReadline; | 131 | exports.parserReadline = parserReadline; |
129 | exports.parserWaitForOkOrError = parserWaitForOkOrError; | 132 | exports.parserWaitForOkOrError = parserWaitForOkOrError; |
130 | 133 |
modem-tester.js
1 | const SerialPort = require('serialport'); | 1 | const SerialPort = require('serialport'); |
2 | 2 | ||
3 | const config = require('komodo-sdk/config'); | 3 | const config = require('komodo-sdk/config'); |
4 | const logger = require('komodo-sdk/logger'); | 4 | const logger = require('komodo-sdk/logger'); |
5 | 5 | ||
6 | 6 | ||
7 | const ParserInterByteTimeout = require('@serialport/parser-inter-byte-timeout'); | 7 | const ParserInterByteTimeout = require('@serialport/parser-inter-byte-timeout'); |
8 | 8 | ||
9 | const parsers = require('./lib/serialport-parsers'); | 9 | const parsers = require('./lib/serialport-parsers'); |
10 | const modemCommands = require('./lib/modem-commands'); | 10 | const modemCommands = require('./lib/modem-commands'); |
11 | 11 | ||
12 | const parserInterByteTimeout = new ParserInterByteTimeout({ interval: 1000 }); | 12 | const parserInterByteTimeout = new ParserInterByteTimeout({ interval: 1000 }); |
13 | parserInterByteTimeout.on('data', (data) => { | 13 | parserInterByteTimeout.on('data', (data) => { |
14 | logger.verbose('INCOMING', { parser: 'parserInterByteTimeout', data: data.toString() }); | 14 | logger.verbose('INCOMING', { parser: 'parserInterByteTimeout', data: data.toString() }); |
15 | }); | 15 | }); |
16 | 16 | ||
17 | let port; | 17 | let port; |
18 | 18 | ||
19 | function sleep(ms) { | 19 | function sleep(ms) { |
20 | return new Promise((resolve) => { | 20 | return new Promise((resolve) => { |
21 | setTimeout(() => { | 21 | setTimeout(() => { |
22 | resolve(); | 22 | resolve(); |
23 | }, ms || 0); | 23 | }, ms || 0); |
24 | }); | 24 | }); |
25 | } | 25 | } |
26 | 26 | ||
27 | function writeToPort(data) { | 27 | function writeToPort(data) { |
28 | return new Promise((resolve) => { | 28 | return new Promise((resolve) => { |
29 | port.write(data, (err, bytesWritten) => { | 29 | port.write(data, (err, bytesWritten) => { |
30 | if (err) logger.warn(`ERROR: ${err.toString()}`); | 30 | if (err) logger.warn(`ERROR: ${err.toString()}`); |
31 | 31 | ||
32 | logger.verbose('OUTGOING', { bytesWritten, data: data.toString() }); | 32 | logger.verbose('OUTGOING', { bytesWritten, data: data.toString() }); |
33 | resolve(bytesWritten); | 33 | resolve(bytesWritten); |
34 | }); | 34 | }); |
35 | }); | 35 | }); |
36 | } | 36 | } |
37 | 37 | ||
38 | async function writeToPortDelayed(data, ms) { | 38 | async function writeToPortDelayed(data, ms) { |
39 | await sleep(ms || 500); | 39 | await sleep(ms || 500); |
40 | const result = writeToPort(data); | 40 | const result = writeToPort(data); |
41 | return result; | 41 | return result; |
42 | } | 42 | } |
43 | 43 | ||
44 | port = new SerialPort(config.modem.device, { baudRate: 115200 }, async (err) => { | 44 | port = new SerialPort(config.modem.device, { baudRate: 115200 }, async (err) => { |
45 | if (err) { | 45 | if (err) { |
46 | logger.warn(`Error opening modem. ${err}. Terminating modem ${config.modem.device}.`); | 46 | logger.warn(`Error opening modem. ${err}. Terminating modem ${config.modem.device}.`); |
47 | process.exit(1); | 47 | process.exit(1); |
48 | } | 48 | } |
49 | 49 | ||
50 | await writeToPortDelayed('AT\r'); | 50 | await writeToPortDelayed('AT\r'); |
51 | 51 | ||
52 | const commands = [ | 52 | const commands = [ |
53 | 'AT&FE0\r', | 53 | 'AT&FE0\r', |
54 | 'AT+CGSN\r', | ||
55 | 'AT+CIMI\r', | 54 | 'AT+CIMI\r', |
56 | 'AT+CMGF=0\r', | 55 | 'AT+CMGF=0\r', |
57 | 'AT+CNMI=1,2,0,1,0\r', | 56 | 'AT+CNMI=1,2,0,1,0\r', |
58 | ]; | 57 | ]; |
59 | 58 | ||
60 | const commandsCount = commands.length; | 59 | const commandsCount = commands.length; |
61 | // eslint-disable-next-line no-plusplus | 60 | // eslint-disable-next-line no-plusplus |
62 | for (let i = 0; i < commandsCount; i++) { | 61 | for (let i = 0; i < commandsCount; i++) { |
63 | // eslint-disable-next-line no-await-in-loop | 62 | // eslint-disable-next-line no-await-in-loop |
64 | await writeToPortDelayed(commands[i], 2000); | 63 | await writeToPortDelayed(commands[i], 2000); |
65 | } | 64 | } |
66 | 65 | ||
67 | if (config && config.modem_tester && config.modem_tester.commands | 66 | if (config && config.modem_tester && config.modem_tester.commands |
68 | && config.modem_tester.commands.length) { | 67 | && config.modem_tester.commands.length) { |
69 | const additionalCommandsLength = config.modem_tester.commands.length; | 68 | const additionalCommandsLength = config.modem_tester.commands.length; |
70 | // eslint-disable-next-line no-plusplus | 69 | // eslint-disable-next-line no-plusplus |
71 | for (let i = 0; i < additionalCommandsLength; i++) { | 70 | for (let i = 0; i < additionalCommandsLength; i++) { |
72 | // eslint-disable-next-line no-await-in-loop | 71 | // eslint-disable-next-line no-await-in-loop |
73 | await writeToPortDelayed(config.modem_tester.commands[i], 2000); | 72 | await writeToPortDelayed(config.modem_tester.commands[i], 2000); |
74 | } | 73 | } |
75 | } | 74 | } |
76 | 75 | ||
76 | await modemCommands.queryIMEI(); | ||
77 | await modemCommands.queryCOPS(); | 77 | await modemCommands.queryCOPS(); |
78 | await modemCommands.querySignalQuality(); | 78 | await modemCommands.querySignalQuality(); |
79 | 79 | ||
80 | setInterval(() => { | 80 | setInterval(() => { |
81 | modemCommands.querySignalQuality(); | 81 | modemCommands.querySignalQuality(); |
82 | }, 30000); | 82 | }, 30000); |
83 | }); | 83 | }); |
84 | 84 | ||
85 | parsers.setPort(port); | 85 | parsers.setPort(port); |
86 | modemCommands.setPort(port); | 86 | modemCommands.setPort(port); |
87 | 87 | ||
88 | if (config && config.modem_tester && config.modem_tester.parser === 'regex') { | 88 | if (config && config.modem_tester && config.modem_tester.parser === 'regex') { |
89 | logger.info('Using parserWaitForOkOrError'); | 89 | logger.info('Using parserWaitForOkOrError'); |
90 | port.pipe(parsers.parserWaitForOkOrError); | 90 | port.pipe(parsers.parserWaitForOkOrError); |
91 | } else if (config && config.modem_tester && config.modem_tester.parser === 'interbyte') { | 91 | } else if (config && config.modem_tester && config.modem_tester.parser === 'interbyte') { |
92 | logger.info('Using parserInterByteTimeout'); | 92 | logger.info('Using parserInterByteTimeout'); |
93 | port.pipe(parserInterByteTimeout); | 93 | port.pipe(parserInterByteTimeout); |
94 | } else { | 94 | } else { |
95 | logger.info('Using parserReadline'); | 95 | logger.info('Using parserReadline'); |
96 | port.pipe(parsers.parserReadline); | 96 | port.pipe(parsers.parserReadline); |
97 | } | 97 | } |