Commit 48751f6728b00148d427a07b5884fdec0ca63dc6

Authored by Adhidarma Hadiwinoto
1 parent 4a065542a0
Exists in master

baudRate

Showing 2 changed files with 4 additions and 3 deletions Inline Diff

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