Commit 716a880bb45bbc14c557f6a573353e7260d622d1

Authored by Adhidarma Hadiwinoto
1 parent 3be15c903b
Exists in master

KOMODO_DEBUG_MODEM by config.debug_modem

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