Commit 24c0e1ac19c13d1559f921c0b1e69906c73fdfeb

Authored by Adhidarma Hadiwinoto
1 parent 4d1f361401
Exists in master

Perbaikan deteksi error

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