Commit 5a8de15542c4e63a79efdafe36a96979534118fd

Authored by Adhidarma Hadiwinoto
1 parent b691bfa013
Exists in master

matrix.not_ready_ts dan kawan-kawan

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