Commit 9883a918f3b35125a4a3fd583bd6a7b76b6469f1
1 parent
5b0551633c
Exists in
master
Urutan log
Showing 2 changed files with 5 additions and 9 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 ParserReadline = require('@serialport/parser-readline'); | 4 | const ParserReadline = require('@serialport/parser-readline'); |
5 | const ParserRegex = require('@serialport/parser-regex'); | 5 | const ParserRegex = require('@serialport/parser-regex'); |
6 | 6 | ||
7 | const logger = require('komodo-sdk/logger'); | 7 | const logger = require('komodo-sdk/logger'); |
8 | const mutex = require('./mutex-common'); | 8 | const mutex = require('./mutex-common'); |
9 | const parsers = require('./serialport-parsers'); | 9 | const parsers = require('./serialport-parsers'); |
10 | const modemInfo = require('./modem-info'); | 10 | const modemInfo = require('./modem-info'); |
11 | 11 | ||
12 | let port; | 12 | let port; |
13 | 13 | ||
14 | function writeToPort(data) { | 14 | function writeToPort(data) { |
15 | return new Promise((resolve) => { | 15 | return new Promise((resolve) => { |
16 | port.write(data, (err, bytesWritten) => { | 16 | port.write(data, (err, bytesWritten) => { |
17 | if (err) logger.warn(`ERROR: ${err.toString()}`); | 17 | if (err) logger.warn(`ERROR: ${err.toString()}`); |
18 | 18 | ||
19 | logger.verbose('OUTGOING', { bytesWritten, data: data.toString() }); | 19 | logger.verbose('OUTGOING', { data: data.toString(), bytesWritten, err }); |
20 | resolve(bytesWritten); | 20 | resolve(bytesWritten); |
21 | }); | 21 | }); |
22 | }); | 22 | }); |
23 | } | 23 | } |
24 | 24 | ||
25 | exports.writeToPortAndWaitForReadline = function writeToPortAndWaitForReadline(cmd, lockName) { | 25 | exports.writeToPortAndWaitForReadline = function writeToPortAndWaitForReadline(cmd, lockName) { |
26 | return new Promise(async (resolve) => { | 26 | return new Promise(async (resolve) => { |
27 | const parser = new ParserReadline({ delimiter: parsers.PARSER_READLINE_DELIMITER }); | 27 | const parser = new ParserReadline({ delimiter: parsers.PARSER_READLINE_DELIMITER }); |
28 | parser.on('data', (data) => { | 28 | parser.on('data', (data) => { |
29 | port.unpipe(parser); | 29 | port.unpipe(parser); |
30 | mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim()); | 30 | mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim()); |
31 | resolve(data); | 31 | resolve(data); |
32 | }); | 32 | }); |
33 | 33 | ||
34 | await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim()); | 34 | await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim()); |
35 | port.pipe(parser); | 35 | port.pipe(parser); |
36 | await writeToPort(cmd); | 36 | await writeToPort(cmd); |
37 | }); | 37 | }); |
38 | }; | 38 | }; |
39 | 39 | ||
40 | exports.writeToPortAndWaitForOkOrError = function writeToPortAndWaitForOkOrError(cmd, lockName) { | 40 | exports.writeToPortAndWaitForOkOrError = function writeToPortAndWaitForOkOrError(cmd, lockName) { |
41 | const parserName = 'customParserWaitForOkOrError'; | ||
42 | return new Promise(async (resolve) => { | 41 | return new Promise(async (resolve) => { |
43 | const parser = new ParserRegex({ regex: /(?:OK|ERROR)\r\n/ }); | 42 | const parser = new ParserRegex({ regex: /(?:OK|ERROR)\r\n/ }); |
44 | parser.on('data', (data) => { | 43 | parser.on('data', (data) => { |
45 | logger.verbose('INCOMING', { parser: parserName, data: data.toString() }); | ||
46 | logger.verbose(`Unpiping ${parserName}`); | ||
47 | port.unpipe(parser); | 44 | port.unpipe(parser); |
48 | mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim()); | 45 | mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim()); |
49 | resolve(data); | 46 | resolve(data); |
50 | }); | 47 | }); |
51 | 48 | ||
52 | await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim()); | 49 | await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim()); |
53 | logger.verbose(`Piping ${parserName}`); | ||
54 | port.pipe(parser); | 50 | port.pipe(parser); |
55 | await writeToPort(cmd); | 51 | await writeToPort(cmd); |
56 | }); | 52 | }); |
57 | }; | 53 | }; |
58 | 54 | ||
59 | exports.sleep = function sleep(ms) { | 55 | exports.sleep = function sleep(ms) { |
60 | return new Promise((resolve) => { | 56 | return new Promise((resolve) => { |
61 | setTimeout(() => { | 57 | setTimeout(() => { |
62 | resolve(); | 58 | resolve(); |
63 | }, ms || 0); | 59 | }, ms || 0); |
64 | }); | 60 | }); |
65 | }; | 61 | }; |
66 | 62 | ||
67 | 63 | ||
68 | exports.setPort = function setPort(val) { | 64 | exports.setPort = function setPort(val) { |
69 | port = val; | 65 | port = val; |
70 | }; | 66 | }; |
71 | 67 | ||
72 | exports.querySignalQuality = function querySignalQuality() { | 68 | exports.querySignalQuality = function querySignalQuality() { |
73 | return new Promise(async (resolve) => { | 69 | return new Promise(async (resolve) => { |
74 | if (!mutex.tryLock(MUTEX_COMMAND, 'querySignalQuality')) { | 70 | if (!mutex.tryLock(MUTEX_COMMAND, 'querySignalQuality')) { |
75 | resolve(false); | 71 | resolve(false); |
76 | return; | 72 | return; |
77 | } | 73 | } |
78 | 74 | ||
79 | await writeToPort('AT+CSQ\r'); | 75 | await writeToPort('AT+CSQ\r'); |
80 | mutex.unlock(MUTEX_COMMAND, 'querySignalQuality'); | 76 | mutex.unlock(MUTEX_COMMAND, 'querySignalQuality'); |
81 | resolve(true); | 77 | resolve(true); |
82 | }); | 78 | }); |
83 | }; | 79 | }; |
84 | 80 | ||
85 | exports.queryCOPS = function querySignalQuality() { | 81 | exports.queryCOPS = function querySignalQuality() { |
86 | return new Promise(async (resolve) => { | 82 | return new Promise(async (resolve) => { |
87 | await mutex.lock(MUTEX_COMMAND, 'queryCOPS'); | 83 | await mutex.lock(MUTEX_COMMAND, 'queryCOPS'); |
88 | await writeToPort('AT+COPS?\r'); | 84 | await writeToPort('AT+COPS?\r'); |
89 | mutex.unlock(MUTEX_COMMAND, 'queryCOPS'); | 85 | mutex.unlock(MUTEX_COMMAND, 'queryCOPS'); |
90 | resolve(true); | 86 | resolve(true); |
91 | }); | 87 | }); |
92 | }; | 88 | }; |
93 | 89 | ||
94 | exports.queryIMEI = function queryIMEI() { | 90 | exports.queryIMEI = function queryIMEI() { |
95 | return new Promise(async (resolve) => { | 91 | return new Promise(async (resolve) => { |
96 | const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); | 92 | const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); |
97 | parser.on('data', (data) => { | 93 | parser.on('data', (data) => { |
98 | logger.verbose('INCOMING', { parser: 'parserIMEI', data: data.toString() }); | 94 | logger.verbose('INCOMING', { data: data.toString(), parser: 'parserIMEI' }); |
99 | port.unpipe(parser); | 95 | port.unpipe(parser); |
100 | mutex.unlock(MUTEX_COMMAND, 'queryIMEI'); | 96 | mutex.unlock(MUTEX_COMMAND, 'queryIMEI'); |
101 | modemInfo.imei = data.toString().trim(); | 97 | modemInfo.imei = data.toString().trim(); |
102 | resolve(modemInfo.imei); | 98 | resolve(modemInfo.imei); |
103 | }); | 99 | }); |
104 | 100 | ||
105 | await mutex.lock(MUTEX_COMMAND, 'queryIMEI'); | 101 | await mutex.lock(MUTEX_COMMAND, 'queryIMEI'); |
106 | 102 | ||
107 | port.pipe(parser); | 103 | port.pipe(parser); |
108 | await writeToPort('AT+CGSN\r'); | 104 | await writeToPort('AT+CGSN\r'); |
109 | }); | 105 | }); |
110 | }; | 106 | }; |
111 | 107 | ||
112 | exports.queryIMSI = function queryIMSI() { | 108 | exports.queryIMSI = function queryIMSI() { |
113 | return new Promise(async (resolve) => { | 109 | return new Promise(async (resolve) => { |
114 | const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); | 110 | const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); |
115 | parser.on('data', (data) => { | 111 | parser.on('data', (data) => { |
116 | logger.verbose('INCOMING', { parser: 'parserIMSI', data: data.toString() }); | 112 | logger.verbose('INCOMING', { data: data.toString(), parser: 'parserIMSI' }); |
117 | port.unpipe(parser); | 113 | port.unpipe(parser); |
118 | mutex.unlock(MUTEX_COMMAND, 'queryIMSI'); | 114 | mutex.unlock(MUTEX_COMMAND, 'queryIMSI'); |
119 | modemInfo.imsi = data.toString().trim(); | 115 | modemInfo.imsi = data.toString().trim(); |
120 | resolve(modemInfo.imsi); | 116 | resolve(modemInfo.imsi); |
121 | }); | 117 | }); |
122 | 118 | ||
123 | await mutex.lock(MUTEX_COMMAND, 'queryIMSI'); | 119 | await mutex.lock(MUTEX_COMMAND, 'queryIMSI'); |
124 | 120 | ||
125 | port.pipe(parser); | 121 | port.pipe(parser); |
126 | await writeToPort('AT+CIMI\r'); | 122 | await writeToPort('AT+CIMI\r'); |
127 | }); | 123 | }); |
128 | }; | 124 | }; |
129 | 125 |
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', { data: `${data.toString()}${PARSER_READLINE_DELIMITER}`, parser: 'parserReadLine' }); |
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', { data: data.toString(), parser: 'parserWaitForOkOrError' }); |
125 | }); | 125 | }); |
126 | 126 | ||
127 | 127 | ||
128 | exports.PARSER_READLINE_DELIMITER = PARSER_READLINE_DELIMITER; | 128 | exports.PARSER_READLINE_DELIMITER = PARSER_READLINE_DELIMITER; |
129 | exports.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = PARSER_WAIT_FOR_OK_OR_ERROR_REGEX; | 129 | exports.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = PARSER_WAIT_FOR_OK_OR_ERROR_REGEX; |
130 | 130 | ||
131 | exports.parserReadline = parserReadline; | 131 | exports.parserReadline = parserReadline; |
132 | exports.parserWaitForOkOrError = parserWaitForOkOrError; | 132 | exports.parserWaitForOkOrError = parserWaitForOkOrError; |
133 | 133 |