Commit 37d0dbf8ab6b30b8e375ef7a1c6bab0888638536
1 parent
1d6334a9bf
Exists in
master
Tangkap CMS ERROR
Showing 1 changed file with 3 additions and 0 deletions Inline Diff
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 = /\n(?:OK|ERROR)\r\n/; | 2 | const PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = /\n(?:OK|ERROR)\r\n/; |
3 | 3 | ||
4 | const moment = require('moment'); | 4 | const moment = require('moment'); |
5 | const nodePdu = require('node-pdu'); | 5 | const nodePdu = require('node-pdu'); |
6 | // const pdu = require('pdu'); | 6 | // const pdu = require('pdu'); |
7 | const ParserReadline = require('@serialport/parser-readline'); | 7 | const ParserReadline = require('@serialport/parser-readline'); |
8 | const ParserRegex = require('@serialport/parser-regex'); | 8 | const ParserRegex = require('@serialport/parser-regex'); |
9 | 9 | ||
10 | const logger = require('komodo-sdk/logger'); | 10 | const logger = require('komodo-sdk/logger'); |
11 | 11 | ||
12 | const dbCops = require('./db-cops'); | 12 | const dbCops = require('./db-cops'); |
13 | const modemInfo = require('./modem-info'); | 13 | const modemInfo = require('./modem-info'); |
14 | 14 | ||
15 | let port; | 15 | let port; |
16 | 16 | ||
17 | exports.setPort = function setPort(val) { | 17 | exports.setPort = function setPort(val) { |
18 | logger.info('SERIALPORT-PARSERS: setting port'); | 18 | logger.info('SERIALPORT-PARSERS: setting port'); |
19 | port = val; | 19 | port = val; |
20 | }; | 20 | }; |
21 | 21 | ||
22 | exports.getPort = function getPort() { | 22 | exports.getPort = function getPort() { |
23 | return port; | 23 | return port; |
24 | }; | 24 | }; |
25 | 25 | ||
26 | let ussdCallback = null; | 26 | let ussdCallback = null; |
27 | function setUssdCallback(cb) { | 27 | function setUssdCallback(cb) { |
28 | ussdCallback = cb; | 28 | ussdCallback = cb; |
29 | } | 29 | } |
30 | exports.setUssdCallback = setUssdCallback; | 30 | exports.setUssdCallback = setUssdCallback; |
31 | 31 | ||
32 | let smsSentCallback = null; | 32 | let smsSentCallback = null; |
33 | function setSmsSentCallback(cb) { | 33 | function setSmsSentCallback(cb) { |
34 | smsSentCallback = cb; | 34 | smsSentCallback = cb; |
35 | } | 35 | } |
36 | exports.setSmsSentCallback = setSmsSentCallback; | 36 | exports.setSmsSentCallback = setSmsSentCallback; |
37 | 37 | ||
38 | function isAlphaNumeric(str) { | 38 | function isAlphaNumeric(str) { |
39 | const len = str.length; | 39 | const len = str.length; |
40 | // eslint-disable-next-line no-plusplus | 40 | // eslint-disable-next-line no-plusplus |
41 | for (let i = 0; i < len; i++) { | 41 | for (let i = 0; i < len; i++) { |
42 | const code = str.charCodeAt(i); | 42 | const code = str.charCodeAt(i); |
43 | if (!(code > 47 && code < 58) // numeric (0-9) | 43 | if (!(code > 47 && code < 58) // numeric (0-9) |
44 | && !(code > 64 && code < 91) // upper alpha (A-Z) | 44 | && !(code > 64 && code < 91) // upper alpha (A-Z) |
45 | && !(code > 96 && code < 123)) { // lower alpha (a-z) | 45 | && !(code > 96 && code < 123)) { // lower alpha (a-z) |
46 | return false; | 46 | return false; |
47 | } | 47 | } |
48 | } | 48 | } |
49 | return true; | 49 | return true; |
50 | } | 50 | } |
51 | 51 | ||
52 | function parsePdu(_data) { | 52 | function parsePdu(_data) { |
53 | const data = _data && _data.toString().trim().toUpperCase(); | 53 | const data = _data && _data.toString().trim().toUpperCase(); |
54 | 54 | ||
55 | if (!data) return null; | 55 | if (!data) return null; |
56 | if (!isAlphaNumeric(data)) return null; | 56 | if (!isAlphaNumeric(data)) return null; |
57 | 57 | ||
58 | try { | 58 | try { |
59 | const result = nodePdu.parse(data); | 59 | const result = nodePdu.parse(data); |
60 | return result; | 60 | return result; |
61 | } catch (e) { | 61 | } catch (e) { |
62 | return null; | 62 | return null; |
63 | } | 63 | } |
64 | } | 64 | } |
65 | 65 | ||
66 | function onCSQ(data) { | 66 | function onCSQ(data) { |
67 | const val = data.toString().trim().match(/\+CSQ:\s*(.*)/); | 67 | const val = data.toString().trim().match(/\+CSQ:\s*(.*)/); |
68 | if (!val || !val[1]) return null; | 68 | if (!val || !val[1]) return null; |
69 | 69 | ||
70 | const [, signalStrength] = val; | 70 | const [, signalStrength] = val; |
71 | 71 | ||
72 | logger.info('Signal quality extracted', { signalQuality: val[1] }); | 72 | logger.info('Signal quality extracted', { signalQuality: val[1] }); |
73 | 73 | ||
74 | modemInfo.signalStrength = signalStrength; | 74 | modemInfo.signalStrength = signalStrength; |
75 | modemInfo.signalStrengthTs = new Date(); | 75 | modemInfo.signalStrengthTs = new Date(); |
76 | modemInfo.signalStrengthTsReadable = moment(modemInfo.signalStrengthTs).format('YYYY-MM-DD HH:mm:ss'); | 76 | modemInfo.signalStrengthTsReadable = moment(modemInfo.signalStrengthTs).format('YYYY-MM-DD HH:mm:ss'); |
77 | 77 | ||
78 | return signalStrength; | 78 | return signalStrength; |
79 | } | 79 | } |
80 | 80 | ||
81 | function onPduDeliver(data, parsedData) { | 81 | function onPduDeliver(data, parsedData) { |
82 | const from = parsedData.getAddress && parsedData.getAddress().getPhone | 82 | const from = parsedData.getAddress && parsedData.getAddress().getPhone |
83 | ? parsedData.getAddress().getPhone() : null; | 83 | ? parsedData.getAddress().getPhone() : null; |
84 | 84 | ||
85 | const msg = parsedData.getData && parsedData.getData().getData | 85 | const msg = parsedData.getData && parsedData.getData().getData |
86 | ? parsedData.getData().getData() : null; | 86 | ? parsedData.getData().getData() : null; |
87 | 87 | ||
88 | const ts = new Date(parsedData.getScts().getIsoString()); | 88 | const ts = new Date(parsedData.getScts().getIsoString()); |
89 | 89 | ||
90 | logger.verbose('PDU processed', { ts, from, msg }); | 90 | logger.verbose('PDU processed', { ts, from, msg }); |
91 | return { from, msg }; | 91 | return { from, msg }; |
92 | } | 92 | } |
93 | 93 | ||
94 | function onCOPS(data) { | 94 | function onCOPS(data) { |
95 | const val = data.toString().trim().match(/\+COPS:\s*(.*)/); | 95 | const val = data.toString().trim().match(/\+COPS:\s*(.*)/); |
96 | if (!val || !val[1]) return null; | 96 | if (!val || !val[1]) return null; |
97 | 97 | ||
98 | const cops = val[1]; | 98 | const cops = val[1]; |
99 | 99 | ||
100 | if (!cops) return null; | 100 | if (!cops) return null; |
101 | const [mode, format, networkId] = cops.split(','); | 101 | const [mode, format, networkId] = cops.split(','); |
102 | const networkName = networkId ? dbCops[networkId] || networkId : null; | 102 | const networkName = networkId ? dbCops[networkId] || networkId : null; |
103 | 103 | ||
104 | logger.info('COPS extracted', { | 104 | logger.info('COPS extracted', { |
105 | cops, mode, format, networkId, networkName, | 105 | cops, mode, format, networkId, networkName, |
106 | }); | 106 | }); |
107 | 107 | ||
108 | modemInfo.cops = cops; | 108 | modemInfo.cops = cops; |
109 | modemInfo.networkId = networkId || null; | 109 | modemInfo.networkId = networkId || null; |
110 | modemInfo.networkName = networkName || null; | 110 | modemInfo.networkName = networkName || null; |
111 | 111 | ||
112 | return { | 112 | return { |
113 | cops, mode, format, networkId, networkName, | 113 | cops, mode, format, networkId, networkName, |
114 | }; | 114 | }; |
115 | } | 115 | } |
116 | 116 | ||
117 | 117 | ||
118 | function isResultCodeIs(data, resultCode) { | 118 | function isResultCodeIs(data, resultCode) { |
119 | if (!data) return false; | 119 | if (!data) return false; |
120 | const cleanedData = (data.toString() || '').trim(); | 120 | const cleanedData = (data.toString() || '').trim(); |
121 | if (!cleanedData) return false; | 121 | if (!cleanedData) return false; |
122 | 122 | ||
123 | if (resultCode.indexOf('+') !== 0) { | 123 | if (resultCode.indexOf('+') !== 0) { |
124 | // eslint-disable-next-line no-param-reassign | 124 | // eslint-disable-next-line no-param-reassign |
125 | resultCode = `+${resultCode}`; | 125 | resultCode = `+${resultCode}`; |
126 | } | 126 | } |
127 | 127 | ||
128 | if (resultCode.search(/:$/) < 0) { | 128 | if (resultCode.search(/:$/) < 0) { |
129 | // eslint-disable-next-line no-param-reassign | 129 | // eslint-disable-next-line no-param-reassign |
130 | resultCode += ':'; | 130 | resultCode += ':'; |
131 | } | 131 | } |
132 | 132 | ||
133 | return cleanedData.indexOf(resultCode) === 0; | 133 | return cleanedData.indexOf(resultCode) === 0; |
134 | } | 134 | } |
135 | 135 | ||
136 | const parserReadline = new ParserReadline({ delimiter: PARSER_READLINE_DELIMITER }); | 136 | const parserReadline = new ParserReadline({ delimiter: PARSER_READLINE_DELIMITER }); |
137 | parserReadline.on('data', (data) => { | 137 | parserReadline.on('data', (data) => { |
138 | modemInfo.lastReadTs = new Date(); | 138 | modemInfo.lastReadTs = new Date(); |
139 | logger.verbose('INCOMING', { data: `${data.toString()}${PARSER_READLINE_DELIMITER}`, parser: 'parserReadLine' }); | 139 | logger.verbose('INCOMING', { data: `${data.toString()}${PARSER_READLINE_DELIMITER}`, parser: 'parserReadLine' }); |
140 | 140 | ||
141 | if (!data) return; | 141 | if (!data) return; |
142 | 142 | ||
143 | const pduParsed = parsePdu(data); | 143 | const pduParsed = parsePdu(data); |
144 | if (pduParsed && pduParsed.constructor.name !== 'Deliver') { | 144 | if (pduParsed && pduParsed.constructor.name !== 'Deliver') { |
145 | const pduType = pduParsed.getType(); | 145 | const pduType = pduParsed.getType(); |
146 | logger.warn('WARN-9DA32C41: Unknown PDU message type name. PLEASE REPORT IT TO DEVELOPER AT TEKTRANS', { typeName: pduParsed.constructor.name, pduType, data: data.toString().trim() }); | 146 | logger.warn('WARN-9DA32C41: Unknown PDU message type name. PLEASE REPORT IT TO DEVELOPER AT TEKTRANS', { typeName: pduParsed.constructor.name, pduType, data: data.toString().trim() }); |
147 | } | 147 | } |
148 | 148 | ||
149 | if (pduParsed && pduParsed.constructor.name === 'Deliver' && pduParsed.getData().getSize()) { | 149 | if (pduParsed && pduParsed.constructor.name === 'Deliver' && pduParsed.getData().getSize()) { |
150 | const pduType = pduParsed.getType(); | 150 | const pduType = pduParsed.getType(); |
151 | logger.verbose('Got a PDU SMS-DELIVER', { pduType }); | 151 | logger.verbose('Got a PDU SMS-DELIVER', { pduType }); |
152 | onPduDeliver(data, pduParsed); | 152 | onPduDeliver(data, pduParsed); |
153 | } else if (isResultCodeIs(data, 'CSQ')) { | 153 | } else if (isResultCodeIs(data, 'CSQ')) { |
154 | logger.verbose('Got a signal quality report', { data: data.toString() }); | 154 | logger.verbose('Got a signal quality report', { data: data.toString() }); |
155 | onCSQ(data); | 155 | onCSQ(data); |
156 | } else if (isResultCodeIs(data, 'COPS:')) { | 156 | } else if (isResultCodeIs(data, 'COPS:')) { |
157 | logger.verbose('Got a COPS report', { data: data.toString() }); | 157 | logger.verbose('Got a COPS report', { data: data.toString() }); |
158 | onCOPS(data); | 158 | onCOPS(data); |
159 | } else if (isResultCodeIs(data, 'CMT')) { | 159 | } else if (isResultCodeIs(data, 'CMT')) { |
160 | logger.verbose('Got a new message report', { data: data.toString() }); | 160 | logger.verbose('Got a new message report', { data: data.toString() }); |
161 | } else if (isResultCodeIs(data, 'CMTI')) { | 161 | } else if (isResultCodeIs(data, 'CMTI')) { |
162 | logger.verbose('Got a new message notification report', { data: data.toString() }); | 162 | logger.verbose('Got a new message notification report', { data: data.toString() }); |
163 | } else if (isResultCodeIs(data, 'CUSD')) { | 163 | } else if (isResultCodeIs(data, 'CUSD')) { |
164 | logger.verbose('Got a USSD command response', { data: data.toString() }); | 164 | logger.verbose('Got a USSD command response', { data: data.toString() }); |
165 | if (typeof ussdCallback === 'function') { | 165 | if (typeof ussdCallback === 'function') { |
166 | logger.verbose('Calling USSD callback'); | 166 | logger.verbose('Calling USSD callback'); |
167 | ussdCallback(data.toString()); | 167 | ussdCallback(data.toString()); |
168 | } else { | 168 | } else { |
169 | logger.verbose('Skip unwanted USSD response'); | 169 | logger.verbose('Skip unwanted USSD response'); |
170 | } | 170 | } |
171 | } else if (isResultCodeIs(data, 'CMGS')) { | 171 | } else if (isResultCodeIs(data, 'CMGS')) { |
172 | logger.verbose('Got CMGS report', { data: data.toString() }); | 172 | logger.verbose('Got CMGS report', { data: data.toString() }); |
173 | if (typeof smsSentCallback === 'function') smsSentCallback(data.toString()); | 173 | if (typeof smsSentCallback === 'function') smsSentCallback(data.toString()); |
174 | } else if (isResultCodeIs(data, 'CMS ERROR')) { | ||
175 | logger.verbose('Got CMS ERROR report', { data: data.toString() }); | ||
176 | if (typeof smsSentCallback === 'function') smsSentCallback(data.toString()); | ||
174 | } | 177 | } |
175 | }); | 178 | }); |
176 | 179 | ||
177 | const parserWaitForOkOrError = new ParserRegex({ regex: PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); | 180 | const parserWaitForOkOrError = new ParserRegex({ regex: PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); |
178 | parserWaitForOkOrError.on('data', (data) => { | 181 | parserWaitForOkOrError.on('data', (data) => { |
179 | logger.verbose('INCOMING', { data: data.toString(), parser: 'parserWaitForOkOrError' }); | 182 | logger.verbose('INCOMING', { data: data.toString(), parser: 'parserWaitForOkOrError' }); |
180 | }); | 183 | }); |
181 | 184 | ||
182 | 185 | ||
183 | exports.PARSER_READLINE_DELIMITER = PARSER_READLINE_DELIMITER; | 186 | exports.PARSER_READLINE_DELIMITER = PARSER_READLINE_DELIMITER; |
184 | exports.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = PARSER_WAIT_FOR_OK_OR_ERROR_REGEX; | 187 | exports.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = PARSER_WAIT_FOR_OK_OR_ERROR_REGEX; |
185 | 188 | ||
186 | exports.parserReadline = parserReadline; | 189 | exports.parserReadline = parserReadline; |
187 | exports.parserWaitForOkOrError = parserWaitForOkOrError; | 190 | exports.parserWaitForOkOrError = parserWaitForOkOrError; |
188 | 191 |