Commit 04b85c0934d829e52c499fbc6701138fdf0b0030

Authored by Adhidarma Hadiwinoto
1 parent c5ab35e0ac
Exists in master

Register modem before init

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