Commit a3c951622ffb6a5389e342de9077192bbfb1f118

Authored by Adhidarma Hadiwinoto
1 parent 7ac39c3b10
Exists in master

Delay before modem ready

Showing 1 changed file with 9 additions and 2 deletions Inline Diff

lib/partner-mkios.js
1 "use strict"; 1 "use strict";
2 2
3 const moment = require('moment'); 3 const moment = require('moment');
4 4
5 const Modem = require('./modem'); 5 const Modem = require('./modem');
6 6
7 const pullgw = require('komodo-sdk/gateway/pull'); 7 const pullgw = require('komodo-sdk/gateway/pull');
8 8
9 const config = require('komodo-sdk/config'); 9 const config = require('komodo-sdk/config');
10 const logger = require('komodo-sdk/logger'); 10 const logger = require('komodo-sdk/logger');
11 const matrix = require('komodo-sdk/matrix'); 11 const matrix = require('komodo-sdk/matrix');
12 12
13 const modemDashboard = require('./modem-dashboard'); 13 const modemDashboard = require('./modem-dashboard');
14 14
15 if (config && config.debug_modem) { 15 if (config && config.debug_modem) {
16 process.env.KOMODO_DEBUG_MODEM=1; 16 process.env.KOMODO_DEBUG_MODEM=1;
17 } 17 }
18 18
19 if (!config || !config.partner || !config.partner.pin) { 19 if (!config || !config.partner || !config.partner.pin) {
20 logger.warn('Undefined PIN'); 20 logger.warn('Undefined PIN');
21 process.exit(1); 21 process.exit(1);
22 } 22 }
23 23
24 matrix.modem = {}; 24 matrix.modem = {};
25 matrix.not_ready = true; 25 matrix.not_ready = true;
26 matrix.stock = {}; 26 matrix.stock = {};
27 27
28 const db = require('./local-db').getConnection(); 28 const db = require('./local-db').getConnection();
29 const pendingArchive = require('./pending-archive'); 29 const pendingArchive = require('./pending-archive');
30 const patternMatcher = require('./pattern-rule-matcher'); 30 const patternMatcher = require('./pattern-rule-matcher');
31 const smsHandler = require('./sms-handler'); 31 const smsHandler = require('./sms-handler');
32 32
33 const modem = new Modem(config.partner.modem.dev, {baudRate: 115200}); 33 const modem = new Modem(config.partner.modem.dev, {baudRate: 115200});
34 34
35 const resumeHandlers = {}; 35 const resumeHandlers = {};
36 36
37 let last_trx_id = null; 37 let last_trx_id = null;
38 38
39 modem.on('open', function() { 39 modem.on('open', function() {
40 logger.info('Modem opened'); 40 logger.info('Modem opened');
41 41
42 const ussd_command = '*776*' + config.partner.pin + '#'; 42 const ussd_command = '*776*' + config.partner.pin + '#';
43 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) { 43 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) {
44 if (err) { 44 if (err) {
45 logger.warn('Error inserting ussd command (stock check) to local database', {err: err}); 45 logger.warn('Error inserting ussd command (stock check) to local database', {err: err});
46 } 46 }
47 }); 47 });
48 48
49 modem.sendUSSD(ussd_command, function() {}); 49 modem.sendUSSD(ussd_command, function() {});
50 }) 50 })
51 51
52 modem.on('imsi', function(imsi) { 52 modem.on('imsi', function(imsi) {
53 logger.verbose('IMSI: ' + imsi); 53 logger.verbose('IMSI: ' + imsi);
54 matrix.modem.imsi = imsi; 54 matrix.modem.imsi = imsi;
55 }) 55 })
56 56
57 57
58 function onIncomingSMS(sms) { 58 function onIncomingSMS(sms) {
59 logger.info('Incoming SMS', {sms: sms}); 59 logger.info('Incoming SMS', {sms: sms});
60 db.run("INSERT INTO sms VALUES (?, ?, 'IN', ?, ?, ?)", sms.created, moment(sms.created).format('YYYY-MM-DD'), matrix.modem.imsi, sms.sender, sms.msg, function(err) { 60 db.run("INSERT INTO sms VALUES (?, ?, 'IN', ?, ?, ?)", sms.created, moment(sms.created).format('YYYY-MM-DD'), matrix.modem.imsi, sms.sender, sms.msg, function(err) {
61 if (err) { 61 if (err) {
62 logger.warn('Error inserting sms to local database', {err: err}); 62 logger.warn('Error inserting sms to local database', {err: err});
63 } 63 }
64 }); 64 });
65 65
66 if (!smsHandler.isAllowedSender(sms.sender)) { 66 if (!smsHandler.isAllowedSender(sms.sender)) {
67 logger.verbose('Ignoring SMS from unknown sender', {sender: sms.sender}); 67 logger.verbose('Ignoring SMS from unknown sender', {sender: sms.sender});
68 return; 68 return;
69 } 69 }
70 70
71 const stocks = smsHandler.getMultiStockBalance(sms.msg); 71 const stocks = smsHandler.getMultiStockBalance(sms.msg);
72 if (stocks && Array.isArray(stocks) && stocks.length) { 72 if (stocks && Array.isArray(stocks) && stocks.length) {
73 stocks.forEach(function(stock) { 73 stocks.forEach(function(stock) {
74 const vals = stock.split('='); 74 const vals = stock.split('=');
75 updateStock(vals[0], vals[1]); 75 updateStock(vals[0], vals[1]);
76 }) 76 })
77 } 77 }
78 else { 78 else {
79 const stock = smsHandler.getStockBalance(sms.msg); 79 const stock = smsHandler.getStockBalance(sms.msg);
80 if (stock.name && stock.balance) { 80 if (stock.name && stock.balance) {
81 updateStock(stock.name, stock.balance); 81 updateStock(stock.name, stock.balance);
82 } 82 }
83 } 83 }
84 84
85 const destination = smsHandler.getDestination(sms.msg); 85 const destination = smsHandler.getDestination(sms.msg);
86 if (!destination) { 86 if (!destination) {
87 logger.verbose('Ignoring SMS with unknown trx destination'); 87 logger.verbose('Ignoring SMS with unknown trx destination');
88 return; 88 return;
89 } 89 }
90 90
91 const product = smsHandler.getProduct(sms.msg); 91 const product = smsHandler.getProduct(sms.msg);
92 if (!product) { 92 if (!product) {
93 logger.verbose('Ignoring SMS with unknown trx product'); 93 logger.verbose('Ignoring SMS with unknown trx product');
94 return; 94 return;
95 } 95 }
96 96
97 const trx_date = smsHandler.getTrxDate(sms.msg); 97 const trx_date = smsHandler.getTrxDate(sms.msg);
98 if (!trx_date) { 98 if (!trx_date) {
99 logger.verbose('Ignoring SMS with unknown trx date'); 99 logger.verbose('Ignoring SMS with unknown trx date');
100 return; 100 return;
101 } 101 }
102 102
103 logger.verbose('SMS message parsed and extracted', {destination: destination, product: product, trx_date: trx_date}); 103 logger.verbose('SMS message parsed and extracted', {destination: destination, product: product, trx_date: trx_date});
104 pendingArchive.get(destination, product, trx_date, function(err, trx_id) { 104 pendingArchive.get(destination, product, trx_date, function(err, trx_id) {
105 if (!trx_id) { 105 if (!trx_id) {
106 logger.verbose('No pending trx suits with SMS', {destination: destination, product: product, trx_date: trx_date}); 106 logger.verbose('No pending trx suits with SMS', {destination: destination, product: product, trx_date: trx_date});
107 return; 107 return;
108 } 108 }
109 109
110 deleteResumeHandler(trx_id); 110 deleteResumeHandler(trx_id);
111 pendingArchive.remove(trx_id); 111 pendingArchive.remove(trx_id);
112 112
113 report({ 113 report({
114 trx_id: trx_id, 114 trx_id: trx_id,
115 rc: smsHandler.getRc(sms.msg) || '68', 115 rc: smsHandler.getRc(sms.msg) || '68',
116 sn: smsHandler.getSn(sms.msg), 116 sn: smsHandler.getSn(sms.msg),
117 message: 'SMS: ' + sms.msg 117 message: 'SMS: ' + sms.msg
118 }) 118 })
119 }) 119 })
120 120
121 } 121 }
122 modem.on('incoming sms', onIncomingSMS); 122 modem.on('incoming sms', onIncomingSMS);
123 123
124 modem.on('signal strength', function(signal_strength) { 124 modem.on('signal strength', function(signal_strength) {
125 matrix.modem.signal_strength = signal_strength; 125 matrix.modem.signal_strength = signal_strength;
126 logger.verbose('Signal strength: ' + signal_strength); 126 logger.verbose('Signal strength: ' + signal_strength);
127 }) 127 })
128 128
129 function onUSSDResponse(data) { 129 function onUSSDResponse(data) {
130 logger.verbose('Got USSD response', {response: data}); 130 logger.verbose('Got USSD response', {response: data});
131 131
132 db.run("INSERT INTO ussd VALUES (?, ?, 'IN', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, data, function(err) { 132 db.run("INSERT INTO ussd VALUES (?, ?, 'IN', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, data, function(err) {
133 if (err) { 133 if (err) {
134 logger.warn('Error inserting ussd response to local database', {err: err}); 134 logger.warn('Error inserting ussd response to local database', {err: err});
135 } 135 }
136 }); 136 });
137 137
138 138
139 if (!last_trx_id) return; 139 if (!last_trx_id) return;
140 140
141 141
142 const rc = getRcFromMessage(data, config.ussd_parser.rc) || '68'; 142 const rc = getRcFromMessage(data, config.ussd_parser.rc) || '68';
143 if (rc !== '68') { 143 if (rc !== '68') {
144 onTrxFinish(last_trx_id); 144 onTrxFinish(last_trx_id);
145 } 145 }
146 146
147 report({ 147 report({
148 trx_id: last_trx_id, 148 trx_id: last_trx_id,
149 rc: rc, 149 rc: rc,
150 sn: getSnFromMessage(data), 150 sn: getSnFromMessage(data),
151 message: data 151 message: data
152 }); 152 });
153 153
154 const stock_name = getStockProductFromMessage(data); 154 const stock_name = getStockProductFromMessage(data);
155 const stock_balance = getStockBalanceFromMesssage(data); 155 const stock_balance = getStockBalanceFromMesssage(data);
156 156
157 updateStock(stock_name, stock_balance); 157 updateStock(stock_name, stock_balance);
158 last_trx_id = null; 158 last_trx_id = null;
159 } 159 }
160 160
161 modem.on('ussd response', onUSSDResponse); 161 modem.on('ussd response', onUSSDResponse);
162 162
163 163
164 logger.info('Opening MODEM'); 164 logger.info('Opening MODEM');
165 modem.open(function(err) { 165 modem.open(function(err) {
166 if (err) { 166 if (err) {
167 logger.warn('Error opening modem port', {err: err}); 167 logger.warn('Error opening modem port', {err: err});
168 process.exit(1); 168 process.exit(1);
169 } 169 }
170 170
171 logger.info('Modem open successfully'); 171 logger.info('Modem open successfully, going to ready in 30 secs');
172 matrix.not_ready = false; 172 setTimeout(
173 function() {
174 matrix.not_ready = false;
175 logger.info('Gateway is ready');
176 },
177 30 * 1000
178 )
179
173 }) 180 })
174 181
175 function updateStock(stock_name, stock_balance) { 182 function updateStock(stock_name, stock_balance) {
176 if (stock_name && (stock_balance !== undefined || stock_balance !== null)) { 183 if (stock_name && (stock_balance !== undefined || stock_balance !== null)) {
177 logger.verbose('Updating stock', {stock_name: stock_name, stock_balance: stock_balance}); 184 logger.verbose('Updating stock', {stock_name: stock_name, stock_balance: stock_balance});
178 185
179 const new_stock_name = config && config.remote_product_alias && config.remote_product_alias[stock_name] ? config.remote_product_alias[stock_name] : stock_name; 186 const new_stock_name = config && config.remote_product_alias && config.remote_product_alias[stock_name] ? config.remote_product_alias[stock_name] : stock_name;
180 187
181 matrix.stock[new_stock_name] = Number(stock_balance); 188 matrix.stock[new_stock_name] = Number(stock_balance);
182 189
183 logger.verbose('Stock balance updated', {stock: matrix.stock}); 190 logger.verbose('Stock balance updated', {stock: matrix.stock});
184 } 191 }
185 } 192 }
186 193
187 function getRcFromMessage(msg, rules) { 194 function getRcFromMessage(msg, rules) {
188 if (!rules || !Array.isArray(rules)) { 195 if (!rules || !Array.isArray(rules)) {
189 return '68'; 196 return '68';
190 } 197 }
191 198
192 for (let rule of rules) { 199 for (let rule of rules) {
193 if (!rule.pattern) return '68'; 200 if (!rule.pattern) return '68';
194 201
195 const re = new RegExp(rule.pattern); 202 const re = new RegExp(rule.pattern);
196 if (msg.search(re) >= 0) { 203 if (msg.search(re) >= 0) {
197 return rule.rc ? rule.rc : '68'; 204 return rule.rc ? rule.rc : '68';
198 } 205 }
199 } 206 }
200 207
201 return '68'; 208 return '68';
202 } 209 }
203 210
204 function getPatternMatchFromMessage(msg, rules) { 211 function getPatternMatchFromMessage(msg, rules) {
205 if (!rules || !Array.isArray(rules)) { 212 if (!rules || !Array.isArray(rules)) {
206 return; 213 return;
207 } 214 }
208 215
209 for (let rule of rules) { 216 for (let rule of rules) {
210 if (!rule.pattern) return; 217 if (!rule.pattern) return;
211 218
212 const re = new RegExp(rule.pattern); 219 const re = new RegExp(rule.pattern);
213 const matches = msg.match(re); 220 const matches = msg.match(re);
214 221
215 if (matches && matches.length >= 2) { 222 if (matches && matches.length >= 2) {
216 return matches[1]; 223 return matches[1];
217 } 224 }
218 } 225 }
219 } 226 }
220 227
221 function getSnFromMessage(msg, rules) { 228 function getSnFromMessage(msg, rules) {
222 return patternMatcher(msg, config.ussd_parser.sn); 229 return patternMatcher(msg, config.ussd_parser.sn);
223 } 230 }
224 231
225 function getStockProductFromMessage(msg, rules) { 232 function getStockProductFromMessage(msg, rules) {
226 return patternMatcher(msg, config.ussd_parser.stock.product); 233 return patternMatcher(msg, config.ussd_parser.stock.product);
227 } 234 }
228 235
229 function getStockBalanceFromMesssage(msg, rules) { 236 function getStockBalanceFromMesssage(msg, rules) {
230 return patternMatcher(msg, config.ussd_parser.stock.balance); 237 return patternMatcher(msg, config.ussd_parser.stock.balance);
231 } 238 }
232 239
233 function suspendPull(trx_id) { 240 function suspendPull(trx_id) {
234 logger.verbose('Set modem to not ready so no other task can be entered and registering delayed resume'); 241 logger.verbose('Set modem to not ready so no other task can be entered and registering delayed resume');
235 matrix.not_ready = true; 242 matrix.not_ready = true;
236 243
237 resumeHandlers['trx' + trx_id] = setTimeout(function() { 244 resumeHandlers['trx' + trx_id] = setTimeout(function() {
238 logger.verbose('Resuming supsended modem', {trx_id: trx_id}); 245 logger.verbose('Resuming supsended modem', {trx_id: trx_id});
239 matrix.not_ready = false; 246 matrix.not_ready = false;
240 report({ 247 report({
241 trx_id: trx_id, 248 trx_id: trx_id,
242 rc: '68', 249 rc: '68',
243 message: 'USSD timeout' 250 message: 'USSD timeout'
244 }) 251 })
245 }, Number(config.partner.modem.suspend_time_ms) || 20 * 1000 ) 252 }, Number(config.partner.modem.suspend_time_ms) || 20 * 1000 )
246 } 253 }
247 254
248 function deleteResumeHandler(trx_id) { 255 function deleteResumeHandler(trx_id) {
249 if (resumeHandlers['trx' + trx_id]) { 256 if (resumeHandlers['trx' + trx_id]) {
250 logger.verbose('Unregistering delayed resume of trx id ' + trx_id); 257 logger.verbose('Unregistering delayed resume of trx id ' + trx_id);
251 clearTimeout(resumeHandlers['trx' + trx_id]); 258 clearTimeout(resumeHandlers['trx' + trx_id]);
252 delete resumeHandlers['trx' + trx_id]; 259 delete resumeHandlers['trx' + trx_id];
253 } 260 }
254 } 261 }
255 262
256 function onTrxFinish(trx_id) { 263 function onTrxFinish(trx_id) {
257 deleteResumeHandler(trx_id); 264 deleteResumeHandler(trx_id);
258 pendingArchive.remove(trx_id); 265 pendingArchive.remove(trx_id);
259 matrix.not_ready = false; 266 matrix.not_ready = false;
260 } 267 }
261 268
262 function buy(task) { 269 function buy(task) {
263 if (task.product === task.remote_product) { 270 if (task.product === task.remote_product) {
264 report({ 271 report({
265 trx_id: task.trx_id, 272 trx_id: task.trx_id,
266 rc: '40', 273 rc: '40',
267 message: 'INTERNAL: Gagal melakukan transaksi. Kode USSD belum terdefinisi.' 274 message: 'INTERNAL: Gagal melakukan transaksi. Kode USSD belum terdefinisi.'
268 }); 275 });
269 return; 276 return;
270 } 277 }
271 278
272 suspendPull(task.trx_id); 279 suspendPull(task.trx_id);
273 last_trx_id = task.trx_id; 280 last_trx_id = task.trx_id;
274 281
275 pendingArchive.put(task, function(err) { 282 pendingArchive.put(task, function(err) {
276 if (err) { 283 if (err) {
277 logger.verbose('Error inserting task to pending archive', {trx_id: task.trx_id, destination: task.destination, product: task.product, err: err}); 284 logger.verbose('Error inserting task to pending archive', {trx_id: task.trx_id, destination: task.destination, product: task.product, err: err});
278 onTrxFinish(task.trx_id); 285 onTrxFinish(task.trx_id);
279 report({ 286 report({
280 trx_id: task.trx_id, 287 trx_id: task.trx_id,
281 rc: '40', 288 rc: '40',
282 message: 'INTERNAL: Gagal melakukan transaksi. Mungkin ada transaksi dengan nomor dengan produk yang sama yang masih diproses. Silahkan ulangi beberapa saat lagi.' 289 message: 'INTERNAL: Gagal melakukan transaksi. Mungkin ada transaksi dengan nomor dengan produk yang sama yang masih diproses. Silahkan ulangi beberapa saat lagi.'
283 }); 290 });
284 291
285 return; 292 return;
286 } 293 }
287 294
288 const ussd_command = task.remote_product.split(',')[0].replace("<DESTINATION>", task.destination).replace("<PIN>", config.partner.pin); 295 const ussd_command = task.remote_product.split(',')[0].replace("<DESTINATION>", task.destination).replace("<PIN>", config.partner.pin);
289 logger.verbose('Going to execute USSD', {trx_id: task.trx_id, destination: task.destination, product: task.product, ussd: ussd_command}); 296 logger.verbose('Going to execute USSD', {trx_id: task.trx_id, destination: task.destination, product: task.product, ussd: ussd_command});
290 297
291 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) { 298 db.run("INSERT INTO ussd VALUES (?, ?, 'OUT', ?, ?)", moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD'), matrix.modem.imsi, 'AT+CUSD=1,"' + ussd_command + '",15', function(err) {
292 if (err) { 299 if (err) {
293 logger.warn('Error inserting ussd command to local database', {err: err}); 300 logger.warn('Error inserting ussd command to local database', {err: err});
294 } 301 }
295 }); 302 });
296 modem.sendUSSD(ussd_command, function() {}); 303 modem.sendUSSD(ussd_command, function() {});
297 }) 304 })
298 305
299 306
300 } 307 }
301 308
302 function report(data) { 309 function report(data) {
303 pullgw.report(data); 310 pullgw.report(data);
304 } 311 }
305 312
306 exports.buy = buy; 313 exports.buy = buy;
307 314