Commit d416919cb0bdbfc7dacc290a2a45cee3971960e1

Authored by Adhidarma Hadiwinoto
1 parent 2f5c3eb620
Exists in master

re-register modem on CSQ

Showing 1 changed file with 1 additions and 0 deletions Inline Diff

1 'use strict'; 1 'use strict';
2 2
3 const INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS = 60000; 3 const INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS = 60000;
4 const MAX_LAST_DATA_AGE_MS = 3 * 60 * 1000; 4 const MAX_LAST_DATA_AGE_MS = 3 * 60 * 1000;
5 const REGEX_WAIT_FOR_OK_OR_ERROR = /\n(?:OK|ERROR)\r\n/; 5 const REGEX_WAIT_FOR_OK_OR_ERROR = /\n(?:OK|ERROR)\r\n/;
6 6
7 const moment = require('moment'); 7 const moment = require('moment');
8 const SerialPort = require('serialport'); 8 const SerialPort = require('serialport');
9 const ParserReadline = require('@serialport/parser-readline'); 9 const ParserReadline = require('@serialport/parser-readline');
10 // const ParserDelimiter = require('@serialport/parser-delimiter'); 10 // const ParserDelimiter = require('@serialport/parser-delimiter');
11 11
12 const ParserRegex = require('@serialport/parser-regex'); 12 const ParserRegex = require('@serialport/parser-regex');
13 13
14 const config = require('komodo-sdk/config'); 14 const config = require('komodo-sdk/config');
15 const logger = require('komodo-sdk/logger'); 15 const logger = require('komodo-sdk/logger');
16 16
17 const mutex = require('./mutex'); 17 const mutex = require('./mutex');
18 const common = require('./common'); 18 const common = require('./common');
19 const sms = require('./sms'); 19 const sms = require('./sms');
20 const dbCops = require('./db-cops'); 20 const dbCops = require('./db-cops');
21 const reportSender = require('./report-sender'); 21 const reportSender = require('./report-sender');
22 const msisdn = require('./msisdn'); 22 const msisdn = require('./msisdn');
23 const registerModem = require('./register-modem'); 23 const registerModem = require('./register-modem');
24 24
25 const modemInfo = { 25 const modemInfo = {
26 device: config.modem.device, 26 device: config.modem.device,
27 manufacturer: null, 27 manufacturer: null,
28 model: null, 28 model: null,
29 imei: null, 29 imei: null,
30 imsi: null, 30 imsi: null,
31 msisdn: null, 31 msisdn: null,
32 cops: null, 32 cops: null,
33 networkId: null, 33 networkId: null,
34 networkName: null, 34 networkName: null,
35 signalStrength: null, 35 signalStrength: null,
36 signalStrengthTs: null, 36 signalStrengthTs: null,
37 signalStrengthTsReadable: null, 37 signalStrengthTsReadable: null,
38 }; 38 };
39 39
40 let lastTs = new Date(); 40 let lastTs = new Date();
41 41
42 const port = new SerialPort(config.modem.device, { baudRate: 115200 }, (err) => { 42 const port = new SerialPort(config.modem.device, { baudRate: 115200 }, (err) => {
43 if (err) { 43 if (err) {
44 logger.warn(`Error opening modem. ${err}. Terminating modem ${config.modem.device}.`); 44 logger.warn(`Error opening modem. ${err}. Terminating modem ${config.modem.device}.`);
45 process.exit(1); 45 process.exit(1);
46 } 46 }
47 }); 47 });
48 48
49 49
50 const parserReadLine = new ParserReadline(); 50 const parserReadLine = new ParserReadline();
51 51
52 const parserWaitForOK = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR }); 52 const parserWaitForOK = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR });
53 parserWaitForOK.on('data', () => { 53 parserWaitForOK.on('data', () => {
54 mutex.releaseLockWaitForCommand(); 54 mutex.releaseLockWaitForCommand();
55 }); 55 });
56 56
57 57
58 port.pipe(parserReadLine); 58 port.pipe(parserReadLine);
59 59
60 function writeToPort(data) { 60 function writeToPort(data) {
61 return new Promise((resolve) => { 61 return new Promise((resolve) => {
62 port.write(data, (err, bytesWritten) => { 62 port.write(data, (err, bytesWritten) => {
63 if (err) logger.warn(`ERROR: ${err.toString()}`); 63 if (err) logger.warn(`ERROR: ${err.toString()}`);
64 logger.verbose(`* OUT: ${data}`); 64 logger.verbose(`* OUT: ${data}`);
65 resolve(bytesWritten); 65 resolve(bytesWritten);
66 }); 66 });
67 }); 67 });
68 } 68 }
69 69
70 // eslint-disable-next-line no-unused-vars 70 // eslint-disable-next-line no-unused-vars
71 async function writeToPortAndWaitForOK(data) { 71 async function writeToPortAndWaitForOK(data) {
72 await mutex.setLockWaitForCommand(); 72 await mutex.setLockWaitForCommand();
73 const result = await writeToPort(data); 73 const result = await writeToPort(data);
74 74
75 await mutex.setLockWaitForCommand(); 75 await mutex.setLockWaitForCommand();
76 mutex.releaseLockWaitForCommand(); 76 mutex.releaseLockWaitForCommand();
77 77
78 return result; 78 return result;
79 } 79 }
80 80
81 async function readSMS(slot) { 81 async function readSMS(slot) {
82 const parserCMGR = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR }); 82 const parserCMGR = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR });
83 parserCMGR.on('data', (data) => { 83 parserCMGR.on('data', (data) => {
84 if (data) { 84 if (data) {
85 try { 85 try {
86 reportSender.incomingSMS(sms.extract(data.toString().trim()), modemInfo); 86 reportSender.incomingSMS(sms.extract(data.toString().trim()), modemInfo);
87 } catch (e) { 87 } catch (e) {
88 logger.warn(`Exception on reporting new message. ${e.toString()}`, { smsObj: e.smsObj, dataFromModem: data }); 88 logger.warn(`Exception on reporting new message. ${e.toString()}`, { smsObj: e.smsObj, dataFromModem: data });
89 89
90 process.exit(0); 90 process.exit(0);
91 } 91 }
92 } 92 }
93 port.unpipe(parserCMGR); 93 port.unpipe(parserCMGR);
94 mutex.releaseLockWaitForCommand(); 94 mutex.releaseLockWaitForCommand();
95 }); 95 });
96 96
97 // const parserCMGD = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK }); 97 // const parserCMGD = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK });
98 const parserCMGD = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR }); 98 const parserCMGD = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR });
99 parserCMGD.on('data', () => { 99 parserCMGD.on('data', () => {
100 port.unpipe(parserCMGD); 100 port.unpipe(parserCMGD);
101 mutex.releaseLockWaitForCommand(); 101 mutex.releaseLockWaitForCommand();
102 }); 102 });
103 103
104 logger.info(`Reading SMS on slot ${slot}`); 104 logger.info(`Reading SMS on slot ${slot}`);
105 await mutex.setLockWaitForCommand(); 105 await mutex.setLockWaitForCommand();
106 port.pipe(parserCMGR); 106 port.pipe(parserCMGR);
107 await writeToPort(`AT+CMGR=${slot}\r`); 107 await writeToPort(`AT+CMGR=${slot}\r`);
108 logger.info(`Finished reading SMS on slot ${slot}`); 108 logger.info(`Finished reading SMS on slot ${slot}`);
109 109
110 logger.info(`Deleting message on slot ${slot}`); 110 logger.info(`Deleting message on slot ${slot}`);
111 await mutex.setLockWaitForCommand(); 111 await mutex.setLockWaitForCommand();
112 port.pipe(parserCMGD); 112 port.pipe(parserCMGD);
113 await writeToPort(`AT+CMGD=${slot}\r`); 113 await writeToPort(`AT+CMGD=${slot}\r`);
114 logger.info('Message processing has completed'); 114 logger.info('Message processing has completed');
115 } 115 }
116 116
117 function onIncomingSMS(data) { 117 function onIncomingSMS(data) {
118 const value = common.extractValueFromReadLineData(data); 118 const value = common.extractValueFromReadLineData(data);
119 if (!value) return; 119 if (!value) return;
120 120
121 const chunks = value.split(','); 121 const chunks = value.split(',');
122 if (!chunks && !chunks[1]) return; 122 if (!chunks && !chunks[1]) return;
123 123
124 const slot = chunks[1]; 124 const slot = chunks[1];
125 125
126 logger.info(`Incoming SMS on slot ${slot}`); 126 logger.info(`Incoming SMS on slot ${slot}`);
127 readSMS(slot); 127 readSMS(slot);
128 } 128 }
129 129
130 function onCOPS(data) { 130 function onCOPS(data) {
131 modemInfo.cops = common.extractValueFromReadLineData(data).trim(); 131 modemInfo.cops = common.extractValueFromReadLineData(data).trim();
132 logger.info(`Connected Network: ${modemInfo.cops}`); 132 logger.info(`Connected Network: ${modemInfo.cops}`);
133 133
134 if (!modemInfo.cops) return; 134 if (!modemInfo.cops) return;
135 135
136 [, , modemInfo.networkId] = modemInfo.cops.split(','); 136 [, , modemInfo.networkId] = modemInfo.cops.split(',');
137 137
138 if (modemInfo.networkId) { 138 if (modemInfo.networkId) {
139 modemInfo.networkName = dbCops[modemInfo.networkId]; 139 modemInfo.networkName = dbCops[modemInfo.networkId];
140 } 140 }
141 } 141 }
142 142
143 parserReadLine.on('data', (data) => { 143 parserReadLine.on('data', (data) => {
144 logger.verbose(`* IN: ${data}`); 144 logger.verbose(`* IN: ${data}`);
145 if (data) { 145 if (data) {
146 lastTs = new Date(); 146 lastTs = new Date();
147 if (data.indexOf('+CSQ: ') === 0) { 147 if (data.indexOf('+CSQ: ') === 0) {
148 const signalStrength = common.extractValueFromReadLineData(data).trim(); 148 const signalStrength = common.extractValueFromReadLineData(data).trim();
149 if (signalStrength) { 149 if (signalStrength) {
150 modemInfo.signalStrength = signalStrength; 150 modemInfo.signalStrength = signalStrength;
151 modemInfo.signalStrengthTs = new Date(); 151 modemInfo.signalStrengthTs = new Date();
152 modemInfo.signalStrengthTsReadable = moment(modemInfo.signalStrengthTs).format('YYYY-MM-DD HH:mm:ss'); 152 modemInfo.signalStrengthTsReadable = moment(modemInfo.signalStrengthTs).format('YYYY-MM-DD HH:mm:ss');
153 logger.info(`Signal strength: ${modemInfo.signalStrength}`); 153 logger.info(`Signal strength: ${modemInfo.signalStrength}`);
154 registerModem(modemInfo);
154 } 155 }
155 } else if (data.indexOf('+CMTI: ') === 0) { 156 } else if (data.indexOf('+CMTI: ') === 0) {
156 onIncomingSMS(data); 157 onIncomingSMS(data);
157 } else if (data.indexOf('+COPS: ') === 0) { 158 } else if (data.indexOf('+COPS: ') === 0) {
158 onCOPS(data); 159 onCOPS(data);
159 } 160 }
160 } 161 }
161 }); 162 });
162 163
163 async function simpleSubCommand(cmd, callback) { 164 async function simpleSubCommand(cmd, callback) {
164 const parser = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR }); 165 const parser = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR });
165 parser.on('data', (data) => { 166 parser.on('data', (data) => {
166 port.unpipe(parser); 167 port.unpipe(parser);
167 mutex.releaseLockWaitForSubCommand(); 168 mutex.releaseLockWaitForSubCommand();
168 169
169 if (data) { 170 if (data) {
170 if (callback) callback(null, data.toString().trim()); 171 if (callback) callback(null, data.toString().trim());
171 } 172 }
172 }); 173 });
173 174
174 return new Promise(async (resolve) => { 175 return new Promise(async (resolve) => {
175 await mutex.setLockWaitForSubCommand(); 176 await mutex.setLockWaitForSubCommand();
176 port.pipe(parser); 177 port.pipe(parser);
177 writeToPort(cmd); 178 writeToPort(cmd);
178 179
179 await mutex.setLockWaitForSubCommand(); 180 await mutex.setLockWaitForSubCommand();
180 mutex.releaseLockWaitForSubCommand(); 181 mutex.releaseLockWaitForSubCommand();
181 182
182 resolve(); 183 resolve();
183 }); 184 });
184 } 185 }
185 186
186 function readManufacturer() { 187 function readManufacturer() {
187 return new Promise((resolve) => { 188 return new Promise((resolve) => {
188 simpleSubCommand('AT+CGMI\r', (err, result) => { 189 simpleSubCommand('AT+CGMI\r', (err, result) => {
189 modemInfo.manufacturer = result; 190 modemInfo.manufacturer = result;
190 logger.info(`Manufacturer: ${result}`); 191 logger.info(`Manufacturer: ${result}`);
191 resolve(result); 192 resolve(result);
192 }); 193 });
193 }); 194 });
194 } 195 }
195 196
196 function readModel() { 197 function readModel() {
197 return new Promise((resolve) => { 198 return new Promise((resolve) => {
198 simpleSubCommand('AT+CGMM\r', (err, result) => { 199 simpleSubCommand('AT+CGMM\r', (err, result) => {
199 modemInfo.model = result; 200 modemInfo.model = result;
200 logger.info(`Model: ${result}`); 201 logger.info(`Model: ${result}`);
201 resolve(result); 202 resolve(result);
202 }); 203 });
203 }); 204 });
204 } 205 }
205 206
206 function readIMEI() { 207 function readIMEI() {
207 return new Promise((resolve) => { 208 return new Promise((resolve) => {
208 simpleSubCommand('AT+CGSN\r', (err, result) => { 209 simpleSubCommand('AT+CGSN\r', (err, result) => {
209 modemInfo.imei = result; 210 modemInfo.imei = result;
210 logger.info(`IMEI: ${result}`); 211 logger.info(`IMEI: ${result}`);
211 resolve(result); 212 resolve(result);
212 }); 213 });
213 }); 214 });
214 } 215 }
215 216
216 function readIMSI() { 217 function readIMSI() {
217 return new Promise((resolve) => { 218 return new Promise((resolve) => {
218 simpleSubCommand('AT+CIMI\r', (err, result) => { 219 simpleSubCommand('AT+CIMI\r', (err, result) => {
219 modemInfo.imsi = result; 220 modemInfo.imsi = result;
220 logger.info(`IMSI: ${result}`); 221 logger.info(`IMSI: ${result}`);
221 222
222 if (result) { 223 if (result) {
223 modemInfo.msisdn = msisdn[result]; 224 modemInfo.msisdn = msisdn[result];
224 if (modemInfo.msisdn) { 225 if (modemInfo.msisdn) {
225 logger.info(`MSISDN: ${modemInfo.msisdn}`); 226 logger.info(`MSISDN: ${modemInfo.msisdn}`);
226 } 227 }
227 } else { 228 } else {
228 logger.warn(`IMSI not detected. Please insert a sim card to your modem. Terminating ${config.modem.device}.`); 229 logger.warn(`IMSI not detected. Please insert a sim card to your modem. Terminating ${config.modem.device}.`);
229 process.exit(2); 230 process.exit(2);
230 } 231 }
231 resolve(result); 232 resolve(result);
232 }); 233 });
233 }); 234 });
234 } 235 }
235 236
236 function readCOPS() { 237 function readCOPS() {
237 return new Promise((resolve) => { 238 return new Promise((resolve) => {
238 simpleSubCommand('AT+COPS?\r', (err, result) => { 239 simpleSubCommand('AT+COPS?\r', (err, result) => {
239 resolve(result); 240 resolve(result);
240 }); 241 });
241 }); 242 });
242 } 243 }
243 244
244 function deleteInbox() { 245 function deleteInbox() {
245 return new Promise((resolve) => { 246 return new Promise((resolve) => {
246 simpleSubCommand('AT+CMGD=0,4\r', (err, result) => { 247 simpleSubCommand('AT+CMGD=0,4\r', (err, result) => {
247 resolve(result); 248 resolve(result);
248 }); 249 });
249 }); 250 });
250 } 251 }
251 252
252 async function querySignalStrength() { 253 async function querySignalStrength() {
253 const parser = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR }); 254 const parser = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR });
254 parser.on('data', () => { 255 parser.on('data', () => {
255 port.unpipe(parser); 256 port.unpipe(parser);
256 mutex.releaseLockWaitForCommand(); 257 mutex.releaseLockWaitForCommand();
257 }); 258 });
258 259
259 if (mutex.tryLockWaitForCommand()) { 260 if (mutex.tryLockWaitForCommand()) {
260 port.pipe(parser); 261 port.pipe(parser);
261 await writeToPort('AT+CSQ\r'); 262 await writeToPort('AT+CSQ\r');
262 } 263 }
263 } 264 }
264 265
265 function registerModemToCenterPeriodically() { 266 function registerModemToCenterPeriodically() {
266 registerModem(modemInfo); 267 registerModem(modemInfo);
267 268
268 setInterval(() => { 269 setInterval(() => {
269 registerModem(modemInfo); 270 registerModem(modemInfo);
270 }, 60 * 1000); 271 }, 60 * 1000);
271 } 272 }
272 273
273 async function registerSignalStrengthBackgroundQuery() { 274 async function registerSignalStrengthBackgroundQuery() {
274 logger.info('Registering background signal strength query'); 275 logger.info('Registering background signal strength query');
275 276
276 querySignalStrength(); 277 querySignalStrength();
277 278
278 setInterval(() => { 279 setInterval(() => {
279 querySignalStrength(); 280 querySignalStrength();
280 }, config.interval_beetwen_signal_strength_ms || INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS); 281 }, config.interval_beetwen_signal_strength_ms || INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS);
281 } 282 }
282 283
283 async function sendSMS(destination, msg) { 284 async function sendSMS(destination, msg) {
284 if (typeof destination !== 'string' || typeof msg !== 'string' || !destination.trim() || !msg.trim()) return; 285 if (typeof destination !== 'string' || typeof msg !== 'string' || !destination.trim() || !msg.trim()) return;
285 286
286 const parser = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR }); 287 const parser = new ParserRegex({ regex: REGEX_WAIT_FOR_OK_OR_ERROR });
287 parser.on('data', () => { 288 parser.on('data', () => {
288 port.unpipe(parser); 289 port.unpipe(parser);
289 mutex.releaseLockWaitForSubCommand(); 290 mutex.releaseLockWaitForSubCommand();
290 }); 291 });
291 292
292 logger.verbose('Waiting for command lock to send message'); 293 logger.verbose('Waiting for command lock to send message');
293 await mutex.setLockWaitForCommand(); 294 await mutex.setLockWaitForCommand();
294 295
295 logger.info('Sending message', { destination, msg }); 296 logger.info('Sending message', { destination, msg });
296 297
297 const correctedDestination = `+${destination}`.replace(/^0/, '62').replace(/^\++/, '+'); 298 const correctedDestination = `+${destination}`.replace(/^0/, '62').replace(/^\++/, '+');
298 299
299 logger.verbose('Waiting for lock before set to text mode'); 300 logger.verbose('Waiting for lock before set to text mode');
300 await mutex.setLockWaitForSubCommand(); 301 await mutex.setLockWaitForSubCommand();
301 port.pipe(parser); 302 port.pipe(parser);
302 await writeToPort('AT+CMGF=1\r'); 303 await writeToPort('AT+CMGF=1\r');
303 304
304 logger.verbose('Waiting for lock before writing message'); 305 logger.verbose('Waiting for lock before writing message');
305 await mutex.setLockWaitForSubCommand(); 306 await mutex.setLockWaitForSubCommand();
306 port.pipe(parser); 307 port.pipe(parser);
307 await writeToPort(`AT+CMGS="${correctedDestination}"\n${msg}${Buffer.from([0x1A])}`); 308 await writeToPort(`AT+CMGS="${correctedDestination}"\n${msg}${Buffer.from([0x1A])}`);
308 309
309 await mutex.setLockWaitForSubCommand(); 310 await mutex.setLockWaitForSubCommand();
310 mutex.releaseLockWaitForSubCommand(); 311 mutex.releaseLockWaitForSubCommand();
311 312
312 logger.info('Message has been sent'); 313 logger.info('Message has been sent');
313 314
314 setTimeout(() => { 315 setTimeout(() => {
315 logger.verbose('Releasing command lock'); 316 logger.verbose('Releasing command lock');
316 mutex.releaseLockWaitForCommand(); 317 mutex.releaseLockWaitForCommand();
317 }, 2000); 318 }, 2000);
318 } 319 }
319 320
320 function init() { 321 function init() {
321 setInterval(() => { 322 setInterval(() => {
322 if ((new Date() - lastTs) > MAX_LAST_DATA_AGE_MS) { 323 if ((new Date() - lastTs) > MAX_LAST_DATA_AGE_MS) {
323 logger.warn(`No data for more than ${MAX_LAST_DATA_AGE_MS} ms. Modem might be unresponsive. Terminating modem ${config.modem.device}.`); 324 logger.warn(`No data for more than ${MAX_LAST_DATA_AGE_MS} ms. Modem might be unresponsive. Terminating modem ${config.modem.device}.`);
324 process.exit(0); 325 process.exit(0);
325 } 326 }
326 }, 30 * 1000); 327 }, 30 * 1000);
327 328
328 port.on('open', async () => { 329 port.on('open', async () => {
329 await mutex.setLockWaitForCommand(); 330 await mutex.setLockWaitForCommand();
330 331
331 logger.info('Modem opened'); 332 logger.info('Modem opened');
332 await writeToPort('\r'); 333 await writeToPort('\r');
333 await simpleSubCommand('AT\r'); 334 await simpleSubCommand('AT\r');
334 335
335 logger.info('Initializing modem to factory set'); 336 logger.info('Initializing modem to factory set');
336 await simpleSubCommand('AT&F\r'); 337 await simpleSubCommand('AT&F\r');
337 338
338 logger.info('Disabling echo'); 339 logger.info('Disabling echo');
339 await simpleSubCommand('ATE0\r'); 340 await simpleSubCommand('ATE0\r');
340 341
341 await readCOPS(); 342 await readCOPS();
342 343
343 await readManufacturer(); 344 await readManufacturer();
344 await readModel(); 345 await readModel();
345 await readIMEI(); 346 await readIMEI();
346 await readIMSI(); 347 await readIMSI();
347 348
348 if (!config.disable_delete_inbox_on_startup) { 349 if (!config.disable_delete_inbox_on_startup) {
349 logger.info('Deleting existing messages'); 350 logger.info('Deleting existing messages');
350 await deleteInbox(); 351 await deleteInbox();
351 } 352 }
352 353
353 mutex.releaseLockWaitForCommand(); 354 mutex.releaseLockWaitForCommand();
354 logger.verbose('Init completed'); 355 logger.verbose('Init completed');
355 356
356 registerModemToCenterPeriodically(); 357 registerModemToCenterPeriodically();
357 registerSignalStrengthBackgroundQuery(); 358 registerSignalStrengthBackgroundQuery();
358 }); 359 });
359 } 360 }
360 361
361 init(); 362 init();
362 363
363 exports.modemInfo = modemInfo; 364 exports.modemInfo = modemInfo;
364 exports.sendSMS = sendSMS; 365 exports.sendSMS = sendSMS;
365 366