Commit 5087dec58df0096811b66d2531e8e887bad2aaaa

Authored by Adhidarma Hadiwinoto
1 parent 8b41e82351
Exists in master

ESLINT

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