Commit 35ba3f5746483b0daae0099e37cee4e17f216fc6

Authored by Adhidarma Hadiwinoto
1 parent 71522f4bf8
Exists in master

Proteksi kalo modem gak responsive atau error

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