Commit 1d6334a9bf44e972f534cb11fd6552c5329477be

Authored by Adhidarma Hadiwinoto
1 parent fded2e0c7f
Exists in master

Coba sms error

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

lib/modem-commands/index.js
1 const MUTEX_COMMAND = 'COMMAND'; 1 const MUTEX_COMMAND = 'COMMAND';
2 exports.MUTEX_COMMAND = MUTEX_COMMAND; 2 exports.MUTEX_COMMAND = MUTEX_COMMAND;
3 3
4 const MUTEX_SUBCOMMAND = 'SUBCOMMAND'; 4 const MUTEX_SUBCOMMAND = 'SUBCOMMAND';
5 exports.MUTEX_SUBCOMMAND = MUTEX_SUBCOMMAND; 5 exports.MUTEX_SUBCOMMAND = MUTEX_SUBCOMMAND;
6 6
7 const CTRLZ = '\u001a'; 7 const CTRLZ = '\u001a';
8 8
9 const pdu = require('node-pdu'); 9 const pdu = require('node-pdu');
10 const uuidv1 = require('uuid/v1'); 10 const uuidv1 = require('uuid/v1');
11 11
12 const ParserReadline = require('@serialport/parser-readline'); 12 const ParserReadline = require('@serialport/parser-readline');
13 const ParserRegex = require('@serialport/parser-regex'); 13 const ParserRegex = require('@serialport/parser-regex');
14 const ParserReady = require('@serialport/parser-ready'); 14 const ParserReady = require('@serialport/parser-ready');
15 15
16 const logger = require('komodo-sdk/logger'); 16 const logger = require('komodo-sdk/logger');
17 const mutex = require('../mutex-common'); 17 const mutex = require('../mutex-common');
18 const parsers = require('../serialport-parsers'); 18 const parsers = require('../serialport-parsers');
19 const modemInfo = require('../modem-info'); 19 const modemInfo = require('../modem-info');
20 20
21 let port; 21 let port;
22 22
23 function writeToPort(data) { 23 function writeToPort(data) {
24 return new Promise((resolve) => { 24 return new Promise((resolve) => {
25 modemInfo.lastWriteTs = new Date(); 25 modemInfo.lastWriteTs = new Date();
26 port.write(data, (err, bytesWritten) => { 26 port.write(data, (err, bytesWritten) => {
27 if (err) logger.warn(`ERROR: ${err.toString()}`); 27 if (err) logger.warn(`ERROR: ${err.toString()}`);
28 28
29 logger.verbose('OUTGOING', { data: data.toString(), bytesWritten, err }); 29 logger.verbose('OUTGOING', { data: data.toString(), bytesWritten, err });
30 resolve(bytesWritten); 30 resolve(bytesWritten);
31 }); 31 });
32 }); 32 });
33 } 33 }
34 exports.writeToPort = writeToPort; 34 exports.writeToPort = writeToPort;
35 35
36 function writeToPortAndWaitForReadline(cmd, lockName) { 36 function writeToPortAndWaitForReadline(cmd, lockName) {
37 let resolved = false; 37 let resolved = false;
38 38
39 return new Promise(async (resolve) => { 39 return new Promise(async (resolve) => {
40 const parser = new ParserReadline({ delimiter: parsers.PARSER_READLINE_DELIMITER }); 40 const parser = new ParserReadline({ delimiter: parsers.PARSER_READLINE_DELIMITER });
41 parser.on('data', (data) => { 41 parser.on('data', (data) => {
42 port.unpipe(parser); 42 port.unpipe(parser);
43 mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim()); 43 mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim());
44 if (!resolved) { 44 if (!resolved) {
45 resolved = true; 45 resolved = true;
46 resolve(data); 46 resolve(data);
47 } 47 }
48 }); 48 });
49 49
50 await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim()); 50 await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim());
51 port.pipe(parser); 51 port.pipe(parser);
52 await writeToPort(cmd); 52 await writeToPort(cmd);
53 }); 53 });
54 } 54 }
55 exports.writeToPortAndWaitForReadline = writeToPortAndWaitForReadline; 55 exports.writeToPortAndWaitForReadline = writeToPortAndWaitForReadline;
56 56
57 function writeToPortAndWaitForOkOrError(cmd, lockName) { 57 function writeToPortAndWaitForOkOrError(cmd, lockName) {
58 return new Promise(async (resolve) => { 58 return new Promise(async (resolve) => {
59 const parser = new ParserRegex({ regex: /(?:OK|ERROR)\r\n/ }); 59 const parser = new ParserRegex({ regex: /(?:OK|ERROR)\r\n/ });
60 parser.on('data', (data) => { 60 parser.on('data', (data) => {
61 port.unpipe(parser); 61 port.unpipe(parser);
62 mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim()); 62 mutex.unlock(lockName || MUTEX_COMMAND, cmd.trim());
63 resolve(data); 63 resolve(data);
64 }); 64 });
65 65
66 await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim()); 66 await mutex.lock(lockName || MUTEX_COMMAND, cmd.trim());
67 port.pipe(parser); 67 port.pipe(parser);
68 await writeToPort(cmd); 68 await writeToPort(cmd);
69 }); 69 });
70 } 70 }
71 exports.writeToPortAndWaitForOkOrError = writeToPortAndWaitForOkOrError; 71 exports.writeToPortAndWaitForOkOrError = writeToPortAndWaitForOkOrError;
72 72
73 function sleep(ms) { 73 function sleep(ms) {
74 return new Promise((resolve) => { 74 return new Promise((resolve) => {
75 setTimeout(() => { 75 setTimeout(() => {
76 resolve(); 76 resolve();
77 }, ms || 0); 77 }, ms || 0);
78 }); 78 });
79 } 79 }
80 exports.sleep = sleep; 80 exports.sleep = sleep;
81 81
82 exports.setPort = function setPort(val) { 82 exports.setPort = function setPort(val) {
83 port = val; 83 port = val;
84 }; 84 };
85 85
86 function querySignalQuality() { 86 function querySignalQuality() {
87 return new Promise(async (resolve) => { 87 return new Promise(async (resolve) => {
88 if (!mutex.tryLock(MUTEX_COMMAND, 'querySignalQuality')) { 88 if (!mutex.tryLock(MUTEX_COMMAND, 'querySignalQuality')) {
89 resolve(false); 89 resolve(false);
90 return; 90 return;
91 } 91 }
92 92
93 await writeToPort('AT+CSQ\r'); 93 await writeToPort('AT+CSQ\r');
94 mutex.unlock(MUTEX_COMMAND, 'querySignalQuality'); 94 mutex.unlock(MUTEX_COMMAND, 'querySignalQuality');
95 resolve(true); 95 resolve(true);
96 }); 96 });
97 } 97 }
98 exports.querySignalQuality = querySignalQuality; 98 exports.querySignalQuality = querySignalQuality;
99 99
100 function queryCOPS(lockName) { 100 function queryCOPS(lockName) {
101 return new Promise(async (resolve) => { 101 return new Promise(async (resolve) => {
102 await mutex.lock(lockName || MUTEX_COMMAND, 'queryCOPS'); 102 await mutex.lock(lockName || MUTEX_COMMAND, 'queryCOPS');
103 await writeToPort('AT+COPS?\r'); 103 await writeToPort('AT+COPS?\r');
104 mutex.unlock(lockName || MUTEX_COMMAND, 'queryCOPS'); 104 mutex.unlock(lockName || MUTEX_COMMAND, 'queryCOPS');
105 resolve(true); 105 resolve(true);
106 }); 106 });
107 } 107 }
108 exports.queryCOPS = queryCOPS; 108 exports.queryCOPS = queryCOPS;
109 109
110 function queryCOPSAndSignalQuality(skipOnLocked) { 110 function queryCOPSAndSignalQuality(skipOnLocked) {
111 return new Promise(async (resolve) => { 111 return new Promise(async (resolve) => {
112 if (!skipOnLocked) { 112 if (!skipOnLocked) {
113 await mutex.lock(MUTEX_COMMAND); 113 await mutex.lock(MUTEX_COMMAND);
114 } else if (!mutex.tryLock(MUTEX_COMMAND, 'queryCOPSAndSignalQuality')) { 114 } else if (!mutex.tryLock(MUTEX_COMMAND, 'queryCOPSAndSignalQuality')) {
115 resolve(false); 115 resolve(false);
116 return; 116 return;
117 } 117 }
118 118
119 await writeToPortAndWaitForOkOrError('AT+COPS?\r', MUTEX_SUBCOMMAND); 119 await writeToPortAndWaitForOkOrError('AT+COPS?\r', MUTEX_SUBCOMMAND);
120 await writeToPortAndWaitForOkOrError('AT+CSQ\r', MUTEX_SUBCOMMAND); 120 await writeToPortAndWaitForOkOrError('AT+CSQ\r', MUTEX_SUBCOMMAND);
121 121
122 mutex.unlock(MUTEX_COMMAND, 'queryCopsAndSignalQuality'); 122 mutex.unlock(MUTEX_COMMAND, 'queryCopsAndSignalQuality');
123 resolve(true); 123 resolve(true);
124 }); 124 });
125 } 125 }
126 exports.queryCOPSAndSignalQuality = queryCOPSAndSignalQuality; 126 exports.queryCOPSAndSignalQuality = queryCOPSAndSignalQuality;
127 127
128 function queryIMEI(lockName) { 128 function queryIMEI(lockName) {
129 return new Promise(async (resolve) => { 129 return new Promise(async (resolve) => {
130 const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); 130 const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX });
131 parser.on('data', (data) => { 131 parser.on('data', (data) => {
132 logger.verbose('INCOMING', { data: data.toString(), parser: 'parserIMEI' }); 132 logger.verbose('INCOMING', { data: data.toString(), parser: 'parserIMEI' });
133 port.unpipe(parser); 133 port.unpipe(parser);
134 mutex.unlock(lockName || MUTEX_COMMAND, 'queryIMEI'); 134 mutex.unlock(lockName || MUTEX_COMMAND, 'queryIMEI');
135 modemInfo.imei = data.toString().trim() || null; 135 modemInfo.imei = data.toString().trim() || null;
136 logger.info('IMEI extracted', { imei: modemInfo.imei }); 136 logger.info('IMEI extracted', { imei: modemInfo.imei });
137 resolve(modemInfo.imei); 137 resolve(modemInfo.imei);
138 }); 138 });
139 139
140 await mutex.lock(lockName || MUTEX_COMMAND, 'queryIMEI'); 140 await mutex.lock(lockName || MUTEX_COMMAND, 'queryIMEI');
141 141
142 port.pipe(parser); 142 port.pipe(parser);
143 await writeToPort('AT+CGSN\r'); 143 await writeToPort('AT+CGSN\r');
144 }); 144 });
145 } 145 }
146 exports.queryIMEI = queryIMEI; 146 exports.queryIMEI = queryIMEI;
147 147
148 function queryIMSI(lockName) { 148 function queryIMSI(lockName) {
149 return new Promise(async (resolve) => { 149 return new Promise(async (resolve) => {
150 const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); 150 const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX });
151 parser.on('data', (data) => { 151 parser.on('data', (data) => {
152 logger.verbose('INCOMING', { data: data.toString(), parser: 'parserIMSI' }); 152 logger.verbose('INCOMING', { data: data.toString(), parser: 'parserIMSI' });
153 port.unpipe(parser); 153 port.unpipe(parser);
154 mutex.unlock(lockName || MUTEX_COMMAND, 'queryIMSI'); 154 mutex.unlock(lockName || MUTEX_COMMAND, 'queryIMSI');
155 modemInfo.imsi = data.toString().trim() || null; 155 modemInfo.imsi = data.toString().trim() || null;
156 logger.info('IMSI extracted', { imsi: modemInfo.imsi }); 156 logger.info('IMSI extracted', { imsi: modemInfo.imsi });
157 resolve(modemInfo.imsi); 157 resolve(modemInfo.imsi);
158 }); 158 });
159 159
160 await mutex.lock(lockName || MUTEX_COMMAND, 'queryIMSI'); 160 await mutex.lock(lockName || MUTEX_COMMAND, 'queryIMSI');
161 161
162 port.pipe(parser); 162 port.pipe(parser);
163 await writeToPort('AT+CIMI\r'); 163 await writeToPort('AT+CIMI\r');
164 }); 164 });
165 } 165 }
166 exports.queryIMSI = queryIMSI; 166 exports.queryIMSI = queryIMSI;
167 167
168 exports.queryIMEIAndIMSI = async function queryIMEIAndIMSI() { 168 exports.queryIMEIAndIMSI = async function queryIMEIAndIMSI() {
169 await mutex.lock(MUTEX_COMMAND, 'queryIMEIAndIMSI'); 169 await mutex.lock(MUTEX_COMMAND, 'queryIMEIAndIMSI');
170 170
171 const imei = await queryIMEI(MUTEX_SUBCOMMAND); 171 const imei = await queryIMEI(MUTEX_SUBCOMMAND);
172 const imsi = await queryIMSI(MUTEX_SUBCOMMAND); 172 const imsi = await queryIMSI(MUTEX_SUBCOMMAND);
173 173
174 await mutex.unlock(MUTEX_COMMAND, 'queryIMEIAndIMSI'); 174 await mutex.unlock(MUTEX_COMMAND, 'queryIMEIAndIMSI');
175 return { imei, imsi }; 175 return { imei, imsi };
176 }; 176 };
177 177
178 exports.queryManufacturer = function queryManufacturer(lockName) { 178 exports.queryManufacturer = function queryManufacturer(lockName) {
179 return new Promise(async (resolve) => { 179 return new Promise(async (resolve) => {
180 const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); 180 const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX });
181 parser.on('data', (data) => { 181 parser.on('data', (data) => {
182 logger.verbose('INCOMING', { data: data.toString(), parser: 'parserManufacturer' }); 182 logger.verbose('INCOMING', { data: data.toString(), parser: 'parserManufacturer' });
183 port.unpipe(parser); 183 port.unpipe(parser);
184 mutex.unlock(lockName || MUTEX_COMMAND, 'parserManufacturer'); 184 mutex.unlock(lockName || MUTEX_COMMAND, 'parserManufacturer');
185 modemInfo.manufacturer = data.toString().trim() || null; 185 modemInfo.manufacturer = data.toString().trim() || null;
186 logger.info('Manufacturer extracted', { manufacturer: modemInfo.manufacturer }); 186 logger.info('Manufacturer extracted', { manufacturer: modemInfo.manufacturer });
187 resolve(modemInfo.manufacturer); 187 resolve(modemInfo.manufacturer);
188 }); 188 });
189 189
190 await mutex.lock(lockName || MUTEX_COMMAND, 'queryManufacturer'); 190 await mutex.lock(lockName || MUTEX_COMMAND, 'queryManufacturer');
191 191
192 port.pipe(parser); 192 port.pipe(parser);
193 await writeToPort('AT+CGMI\r'); 193 await writeToPort('AT+CGMI\r');
194 }); 194 });
195 }; 195 };
196 196
197 exports.queryModel = function queryModel(lockName) { 197 exports.queryModel = function queryModel(lockName) {
198 return new Promise(async (resolve) => { 198 return new Promise(async (resolve) => {
199 const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); 199 const parser = new ParserRegex({ regex: parsers.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX });
200 parser.on('data', (data) => { 200 parser.on('data', (data) => {
201 logger.verbose('INCOMING', { data: data.toString(), parser: 'parserModel' }); 201 logger.verbose('INCOMING', { data: data.toString(), parser: 'parserModel' });
202 port.unpipe(parser); 202 port.unpipe(parser);
203 mutex.unlock(lockName || MUTEX_COMMAND, 'parserModel'); 203 mutex.unlock(lockName || MUTEX_COMMAND, 'parserModel');
204 modemInfo.model = data.toString().trim() || null; 204 modemInfo.model = data.toString().trim() || null;
205 logger.info('Model extracted', { model: modemInfo.model }); 205 logger.info('Model extracted', { model: modemInfo.model });
206 resolve(modemInfo.model); 206 resolve(modemInfo.model);
207 }); 207 });
208 208
209 await mutex.lock(lockName || MUTEX_COMMAND, 'queryModel'); 209 await mutex.lock(lockName || MUTEX_COMMAND, 'queryModel');
210 210
211 port.pipe(parser); 211 port.pipe(parser);
212 await writeToPort('AT+CGMM\r'); 212 await writeToPort('AT+CGMM\r');
213 }); 213 });
214 }; 214 };
215 215
216 async function sendCtrlZ() { 216 async function sendCtrlZ() {
217 await writeToPort(CTRLZ); 217 await writeToPort(CTRLZ);
218 } 218 }
219 exports.sendCtrlZ = sendCtrlZ; 219 exports.sendCtrlZ = sendCtrlZ;
220 220
221 async function initATCommands() { 221 async function initATCommands() {
222 await mutex.lock(MUTEX_COMMAND, 'INIT MODEM'); 222 await mutex.lock(MUTEX_COMMAND, 'INIT MODEM');
223 await this.writeToPortAndWaitForOkOrError(`${CTRLZ}ATE0\r`, MUTEX_SUBCOMMAND); 223 await this.writeToPortAndWaitForOkOrError(`${CTRLZ}ATE0\r`, MUTEX_SUBCOMMAND);
224 await this.writeToPortAndWaitForOkOrError('AT+CMGF=0\r', MUTEX_SUBCOMMAND); 224 await this.writeToPortAndWaitForOkOrError('AT+CMGF=0\r', MUTEX_SUBCOMMAND);
225 await this.writeToPortAndWaitForOkOrError('AT+CNMI=1,2,0,1,0\r', MUTEX_SUBCOMMAND); 225 await this.writeToPortAndWaitForOkOrError('AT+CNMI=1,2,0,1,0\r', MUTEX_SUBCOMMAND);
226 mutex.unlock(MUTEX_COMMAND, 'INIT MODEM'); 226 mutex.unlock(MUTEX_COMMAND, 'INIT MODEM');
227 } 227 }
228 exports.initATCommands = initATCommands; 228 exports.initATCommands = initATCommands;
229 229
230 function sendCMGSPdu(pduLength) { 230 function sendCMGSPdu(pduLength) {
231 return new Promise((resolve) => { 231 return new Promise((resolve) => {
232 const parser = new ParserReady({ delimiter: '>' }); 232 const parser = new ParserReady({ delimiter: '>' });
233 parser.on('data', () => { 233 parser.on('data', () => {
234 logger.verbose('Got ">" message prompt, gonna to write PDU message'); 234 logger.verbose('Got ">" message prompt, gonna to write PDU message');
235 port.unpipe(parser); 235 port.unpipe(parser);
236 mutex.unlock(MUTEX_SUBCOMMAND, 'sendSmsPduCommand'); 236 mutex.unlock(MUTEX_SUBCOMMAND, 'sendSmsPduCommand');
237 resolve(true); 237 resolve(true);
238 }); 238 });
239 239
240 mutex.lock(MUTEX_SUBCOMMAND, 'sendSmsPduCommand'); 240 mutex.lock(MUTEX_SUBCOMMAND, 'sendSmsPduCommand');
241 port.pipe(parser); 241 port.pipe(parser);
242 writeToPort(`AT+CMGS=${pduLength}\r`); 242 writeToPort(`AT+CMGS=${pduLength}\r`);
243 }); 243 });
244 } 244 }
245 245
246 exports.sendSMS = function sendSMS(destination, msg) { 246 exports.sendSMS = function sendSMS(destination, msg) {
247 return new Promise(async (resolve) => { 247 return new Promise(async (resolve) => {
248 async function responseHandler(data) {
249 logger.verbose('SMS sent callback called', { data });
250 }
251
248 await mutex.lock(MUTEX_COMMAND, 'sendSMS'); 252 await mutex.lock(MUTEX_COMMAND, 'sendSMS');
253 parsers.setSmsSentCallback(responseHandler);
249 254
250 if (!destination || !destination.trim()) { 255 if (!destination || !destination.trim()) {
251 resolve(false); 256 resolve(false);
252 return; 257 return;
253 } 258 }
254 259
255 if (!msg || !msg.trim()) { 260 if (!msg || !msg.trim()) {
256 resolve(false); 261 resolve(false);
257 return; 262 return;
258 } 263 }
259 264
260 const correctedDestination = `+${destination.replace(/^0/, '62')}`.replace(/^\++/, '+'); 265 const correctedDestination = `+${destination.replace(/^0/, '62')}`.replace(/^\++/, '+');
261 logger.verbose(`Sending sms to ${correctedDestination}`, { msg }); 266 logger.verbose(`Sending sms to ${correctedDestination}`, { msg });
262 267
263 await this.writeToPortAndWaitForOkOrError('AT+CMGF=0\r', MUTEX_SUBCOMMAND); 268 await this.writeToPortAndWaitForOkOrError('AT+CMGF=0\r', MUTEX_SUBCOMMAND);
264 269
265 const submit = pdu.Submit(); 270 const submit = pdu.Submit();
266 submit.setAddress(correctedDestination); 271 submit.setAddress(correctedDestination);
267 submit.setData(msg.trim()); 272 submit.setData(msg.trim());
268 submit.getType().setSrr(0); 273 submit.getType().setSrr(0);
269 274
270 await sendCMGSPdu(Math.floor(submit.toString().length / 2) - 1); 275 await sendCMGSPdu(Math.floor(submit.toString().length / 2) - 1);
271 await writeToPort(`${submit.toString()}${CTRLZ}`); 276 await writeToPortAndWaitForOkOrError(`${submit.toString()}${CTRLZ}`, MUTEX_SUBCOMMAND);
272 277
273 mutex.unlock(MUTEX_COMMAND, 'sendSMS'); 278 mutex.unlock(MUTEX_COMMAND, 'sendSMS');
274 resolve(true); 279 resolve(true);
275 }); 280 });
276 }; 281 };
277 282
278 exports.executeUSSD = function executeUSSD(code, _includeCUSD2, _sessionId) { 283 exports.executeUSSD = function executeUSSD(code, _includeCUSD2, _sessionId) {
279 return new Promise(async (resolve) => { 284 return new Promise(async (resolve) => {
280 const includeCUSD2 = _includeCUSD2 || 0; 285 const includeCUSD2 = _includeCUSD2 || 0;
281 const sessionId = _sessionId || uuidv1(); 286 const sessionId = _sessionId || uuidv1();
282 287
283 async function responseHandler(data) { 288 async function responseHandler(data) {
284 logger.verbose('Processing USSD response', { data }); 289 logger.verbose('Processing USSD response', { data });
285 parsers.setUssdCallback(null); 290 parsers.setUssdCallback(null);
286 291
287 if (includeCUSD2 === 1 || includeCUSD2 === 2) { 292 if (includeCUSD2 === 1 || includeCUSD2 === 2) {
288 await writeToPortAndWaitForOkOrError('AT+CUSD=2\r', MUTEX_SUBCOMMAND); 293 await writeToPortAndWaitForOkOrError('AT+CUSD=2\r', MUTEX_SUBCOMMAND);
289 } 294 }
290 295
291 mutex.unlock(MUTEX_COMMAND, `executeUSSD ${sessionId}`); 296 mutex.unlock(MUTEX_COMMAND, `executeUSSD ${sessionId}`);
292 resolve(data); 297 resolve(data);
293 } 298 }
294 299
295 mutex.lock(MUTEX_COMMAND, `executeUSSD ${sessionId}`); 300 mutex.lock(MUTEX_COMMAND, `executeUSSD ${sessionId}`);
296 parsers.setUssdCallback(responseHandler); 301 parsers.setUssdCallback(responseHandler);
297 302
298 await this.writeToPortAndWaitForOkOrError(`${CTRLZ}AT+CMGF=0\r`, MUTEX_SUBCOMMAND); 303 await this.writeToPortAndWaitForOkOrError(`${CTRLZ}AT+CMGF=0\r`, MUTEX_SUBCOMMAND);
299 304
300 if (includeCUSD2 === -1 || includeCUSD2 === 2) { 305 if (includeCUSD2 === -1 || includeCUSD2 === 2) {
301 await this.writeToPortAndWaitForOkOrError('AT+CUSD=2\r', MUTEX_SUBCOMMAND); 306 await this.writeToPortAndWaitForOkOrError('AT+CUSD=2\r', MUTEX_SUBCOMMAND);
302 } 307 }
303 308
304 await writeToPort(`AT+CUSD=1,"${code}",15\r`, MUTEX_SUBCOMMAND); 309 await writeToPort(`AT+CUSD=1,"${code}",15\r`, MUTEX_SUBCOMMAND);
305 }); 310 });
306 }; 311 };
307 312
lib/serialport-parsers.js
1 const PARSER_READLINE_DELIMITER = '\r\n'; 1 const PARSER_READLINE_DELIMITER = '\r\n';
2 const PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = /\n(?:OK|ERROR)\r\n/; 2 const PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = /\n(?:OK|ERROR)\r\n/;
3 3
4 const moment = require('moment'); 4 const moment = require('moment');
5 const nodePdu = require('node-pdu'); 5 const nodePdu = require('node-pdu');
6 // const pdu = require('pdu'); 6 // const pdu = require('pdu');
7 const ParserReadline = require('@serialport/parser-readline'); 7 const ParserReadline = require('@serialport/parser-readline');
8 const ParserRegex = require('@serialport/parser-regex'); 8 const ParserRegex = require('@serialport/parser-regex');
9 9
10 const logger = require('komodo-sdk/logger'); 10 const logger = require('komodo-sdk/logger');
11 11
12 const dbCops = require('./db-cops'); 12 const dbCops = require('./db-cops');
13 const modemInfo = require('./modem-info'); 13 const modemInfo = require('./modem-info');
14 14
15 let port; 15 let port;
16 16
17 exports.setPort = function setPort(val) { 17 exports.setPort = function setPort(val) {
18 logger.info('SERIALPORT-PARSERS: setting port'); 18 logger.info('SERIALPORT-PARSERS: setting port');
19 port = val; 19 port = val;
20 }; 20 };
21 21
22 exports.getPort = function getPort() { 22 exports.getPort = function getPort() {
23 return port; 23 return port;
24 }; 24 };
25 25
26 let ussdCallback = null; 26 let ussdCallback = null;
27 function setUssdCallback(cb) { 27 function setUssdCallback(cb) {
28 ussdCallback = cb; 28 ussdCallback = cb;
29 } 29 }
30 exports.setUssdCallback = setUssdCallback; 30 exports.setUssdCallback = setUssdCallback;
31 31
32 let smsSentCallback = null;
33 function setSmsSentCallback(cb) {
34 smsSentCallback = cb;
35 }
36 exports.setSmsSentCallback = setSmsSentCallback;
37
32 function isAlphaNumeric(str) { 38 function isAlphaNumeric(str) {
33 const len = str.length; 39 const len = str.length;
34 // eslint-disable-next-line no-plusplus 40 // eslint-disable-next-line no-plusplus
35 for (let i = 0; i < len; i++) { 41 for (let i = 0; i < len; i++) {
36 const code = str.charCodeAt(i); 42 const code = str.charCodeAt(i);
37 if (!(code > 47 && code < 58) // numeric (0-9) 43 if (!(code > 47 && code < 58) // numeric (0-9)
38 && !(code > 64 && code < 91) // upper alpha (A-Z) 44 && !(code > 64 && code < 91) // upper alpha (A-Z)
39 && !(code > 96 && code < 123)) { // lower alpha (a-z) 45 && !(code > 96 && code < 123)) { // lower alpha (a-z)
40 return false; 46 return false;
41 } 47 }
42 } 48 }
43 return true; 49 return true;
44 } 50 }
45 51
46 function parsePdu(_data) { 52 function parsePdu(_data) {
47 const data = _data && _data.toString().trim().toUpperCase(); 53 const data = _data && _data.toString().trim().toUpperCase();
48 54
49 if (!data) return null; 55 if (!data) return null;
50 if (!isAlphaNumeric(data)) return null; 56 if (!isAlphaNumeric(data)) return null;
51 57
52 try { 58 try {
53 const result = nodePdu.parse(data); 59 const result = nodePdu.parse(data);
54 return result; 60 return result;
55 } catch (e) { 61 } catch (e) {
56 return null; 62 return null;
57 } 63 }
58 } 64 }
59 65
60 function onCSQ(data) { 66 function onCSQ(data) {
61 const val = data.toString().trim().match(/\+CSQ:\s*(.*)/); 67 const val = data.toString().trim().match(/\+CSQ:\s*(.*)/);
62 if (!val || !val[1]) return null; 68 if (!val || !val[1]) return null;
63 69
64 const [, signalStrength] = val; 70 const [, signalStrength] = val;
65 71
66 logger.info('Signal quality extracted', { signalQuality: val[1] }); 72 logger.info('Signal quality extracted', { signalQuality: val[1] });
67 73
68 modemInfo.signalStrength = signalStrength; 74 modemInfo.signalStrength = signalStrength;
69 modemInfo.signalStrengthTs = new Date(); 75 modemInfo.signalStrengthTs = new Date();
70 modemInfo.signalStrengthTsReadable = moment(modemInfo.signalStrengthTs).format('YYYY-MM-DD HH:mm:ss'); 76 modemInfo.signalStrengthTsReadable = moment(modemInfo.signalStrengthTs).format('YYYY-MM-DD HH:mm:ss');
71 77
72 return signalStrength; 78 return signalStrength;
73 } 79 }
74 80
75 function onPduDeliver(data, parsedData) { 81 function onPduDeliver(data, parsedData) {
76 const from = parsedData.getAddress && parsedData.getAddress().getPhone 82 const from = parsedData.getAddress && parsedData.getAddress().getPhone
77 ? parsedData.getAddress().getPhone() : null; 83 ? parsedData.getAddress().getPhone() : null;
78 84
79 const msg = parsedData.getData && parsedData.getData().getData 85 const msg = parsedData.getData && parsedData.getData().getData
80 ? parsedData.getData().getData() : null; 86 ? parsedData.getData().getData() : null;
81 87
82 const ts = new Date(parsedData.getScts().getIsoString()); 88 const ts = new Date(parsedData.getScts().getIsoString());
83 89
84 logger.verbose('PDU processed', { ts, from, msg }); 90 logger.verbose('PDU processed', { ts, from, msg });
85 return { from, msg }; 91 return { from, msg };
86 } 92 }
87 93
88 function onCOPS(data) { 94 function onCOPS(data) {
89 const val = data.toString().trim().match(/\+COPS:\s*(.*)/); 95 const val = data.toString().trim().match(/\+COPS:\s*(.*)/);
90 if (!val || !val[1]) return null; 96 if (!val || !val[1]) return null;
91 97
92 const cops = val[1]; 98 const cops = val[1];
93 99
94 if (!cops) return null; 100 if (!cops) return null;
95 const [mode, format, networkId] = cops.split(','); 101 const [mode, format, networkId] = cops.split(',');
96 const networkName = networkId ? dbCops[networkId] || networkId : null; 102 const networkName = networkId ? dbCops[networkId] || networkId : null;
97 103
98 logger.info('COPS extracted', { 104 logger.info('COPS extracted', {
99 cops, mode, format, networkId, networkName, 105 cops, mode, format, networkId, networkName,
100 }); 106 });
101 107
102 modemInfo.cops = cops; 108 modemInfo.cops = cops;
103 modemInfo.networkId = networkId || null; 109 modemInfo.networkId = networkId || null;
104 modemInfo.networkName = networkName || null; 110 modemInfo.networkName = networkName || null;
105 111
106 return { 112 return {
107 cops, mode, format, networkId, networkName, 113 cops, mode, format, networkId, networkName,
108 }; 114 };
109 } 115 }
110 116
111 117
112 function isResultCodeIs(data, resultCode) { 118 function isResultCodeIs(data, resultCode) {
113 if (!data) return false; 119 if (!data) return false;
114 const cleanedData = (data.toString() || '').trim(); 120 const cleanedData = (data.toString() || '').trim();
115 if (!cleanedData) return false; 121 if (!cleanedData) return false;
116 122
117 if (resultCode.indexOf('+') !== 0) { 123 if (resultCode.indexOf('+') !== 0) {
118 // eslint-disable-next-line no-param-reassign 124 // eslint-disable-next-line no-param-reassign
119 resultCode = `+${resultCode}`; 125 resultCode = `+${resultCode}`;
120 } 126 }
121 127
122 if (resultCode.search(/:$/) < 0) { 128 if (resultCode.search(/:$/) < 0) {
123 // eslint-disable-next-line no-param-reassign 129 // eslint-disable-next-line no-param-reassign
124 resultCode += ':'; 130 resultCode += ':';
125 } 131 }
126 132
127 return cleanedData.indexOf(resultCode) === 0; 133 return cleanedData.indexOf(resultCode) === 0;
128 } 134 }
129 135
130 const parserReadline = new ParserReadline({ delimiter: PARSER_READLINE_DELIMITER }); 136 const parserReadline = new ParserReadline({ delimiter: PARSER_READLINE_DELIMITER });
131 parserReadline.on('data', (data) => { 137 parserReadline.on('data', (data) => {
132 modemInfo.lastReadTs = new Date(); 138 modemInfo.lastReadTs = new Date();
133 logger.verbose('INCOMING', { data: `${data.toString()}${PARSER_READLINE_DELIMITER}`, parser: 'parserReadLine' }); 139 logger.verbose('INCOMING', { data: `${data.toString()}${PARSER_READLINE_DELIMITER}`, parser: 'parserReadLine' });
134 140
135 if (!data) return; 141 if (!data) return;
136 142
137 const pduParsed = parsePdu(data); 143 const pduParsed = parsePdu(data);
138 if (pduParsed && pduParsed.constructor.name !== 'Deliver') { 144 if (pduParsed && pduParsed.constructor.name !== 'Deliver') {
139 const pduType = pduParsed.getType(); 145 const pduType = pduParsed.getType();
140 logger.warn('WARN-9DA32C41: Unknown PDU message type name. PLEASE REPORT IT TO DEVELOPER AT TEKTRANS', { typeName: pduParsed.constructor.name, pduType, data: data.toString().trim() }); 146 logger.warn('WARN-9DA32C41: Unknown PDU message type name. PLEASE REPORT IT TO DEVELOPER AT TEKTRANS', { typeName: pduParsed.constructor.name, pduType, data: data.toString().trim() });
141 } 147 }
142 148
143 if (pduParsed && pduParsed.constructor.name === 'Deliver' && pduParsed.getData().getSize()) { 149 if (pduParsed && pduParsed.constructor.name === 'Deliver' && pduParsed.getData().getSize()) {
144 const pduType = pduParsed.getType(); 150 const pduType = pduParsed.getType();
145 logger.verbose('Got a PDU SMS-DELIVER', { pduType }); 151 logger.verbose('Got a PDU SMS-DELIVER', { pduType });
146 onPduDeliver(data, pduParsed); 152 onPduDeliver(data, pduParsed);
147 } else if (isResultCodeIs(data, 'CSQ')) { 153 } else if (isResultCodeIs(data, 'CSQ')) {
148 logger.verbose('Got a signal quality report', { data: data.toString() }); 154 logger.verbose('Got a signal quality report', { data: data.toString() });
149 onCSQ(data); 155 onCSQ(data);
150 } else if (isResultCodeIs(data, 'COPS:')) { 156 } else if (isResultCodeIs(data, 'COPS:')) {
151 logger.verbose('Got a COPS report', { data: data.toString() }); 157 logger.verbose('Got a COPS report', { data: data.toString() });
152 onCOPS(data); 158 onCOPS(data);
153 } else if (isResultCodeIs(data, 'CMT')) { 159 } else if (isResultCodeIs(data, 'CMT')) {
154 logger.verbose('Got a new message report', { data: data.toString() }); 160 logger.verbose('Got a new message report', { data: data.toString() });
155 } else if (isResultCodeIs(data, 'CMTI')) { 161 } else if (isResultCodeIs(data, 'CMTI')) {
156 logger.verbose('Got a new message notification report', { data: data.toString() }); 162 logger.verbose('Got a new message notification report', { data: data.toString() });
157 } else if (isResultCodeIs(data, 'CUSD')) { 163 } else if (isResultCodeIs(data, 'CUSD')) {
158 logger.verbose('Got a USSD command response', { data: data.toString() }); 164 logger.verbose('Got a USSD command response', { data: data.toString() });
159 if (ussdCallback && typeof ussdCallback === 'function') { 165 if (typeof ussdCallback === 'function') {
160 logger.verbose('Calling USSD callback'); 166 logger.verbose('Calling USSD callback');
161 ussdCallback(data.toString()); 167 ussdCallback(data.toString());
162 } else { 168 } else {
163 logger.verbose('Skip unwanted USSD response'); 169 logger.verbose('Skip unwanted USSD response');
164 } 170 }
171 } else if (isResultCodeIs(data, 'CMGS')) {
172 logger.verbose('Got CMGS report', { data: data.toString() });
173 if (typeof smsSentCallback === 'function') smsSentCallback(data.toString());
165 } 174 }
166 }); 175 });
167 176
168 const parserWaitForOkOrError = new ParserRegex({ regex: PARSER_WAIT_FOR_OK_OR_ERROR_REGEX }); 177 const parserWaitForOkOrError = new ParserRegex({ regex: PARSER_WAIT_FOR_OK_OR_ERROR_REGEX });
169 parserWaitForOkOrError.on('data', (data) => { 178 parserWaitForOkOrError.on('data', (data) => {
170 logger.verbose('INCOMING', { data: data.toString(), parser: 'parserWaitForOkOrError' }); 179 logger.verbose('INCOMING', { data: data.toString(), parser: 'parserWaitForOkOrError' });
171 }); 180 });
172 181
173 182
174 exports.PARSER_READLINE_DELIMITER = PARSER_READLINE_DELIMITER; 183 exports.PARSER_READLINE_DELIMITER = PARSER_READLINE_DELIMITER;
175 exports.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = PARSER_WAIT_FOR_OK_OR_ERROR_REGEX; 184 exports.PARSER_WAIT_FOR_OK_OR_ERROR_REGEX = PARSER_WAIT_FOR_OK_OR_ERROR_REGEX;
176 185
177 exports.parserReadline = parserReadline; 186 exports.parserReadline = parserReadline;
178 exports.parserWaitForOkOrError = parserWaitForOkOrError; 187 exports.parserWaitForOkOrError = parserWaitForOkOrError;
179 188
1 const SerialPort = require('serialport'); 1 const SerialPort = require('serialport');
2 2
3 const config = require('komodo-sdk/config'); 3 const config = require('komodo-sdk/config');
4 const logger = require('komodo-sdk/logger'); 4 const logger = require('komodo-sdk/logger');
5 5
6 const ParserInterByteTimeout = require('@serialport/parser-inter-byte-timeout'); 6 const ParserInterByteTimeout = require('@serialport/parser-inter-byte-timeout');
7 7
8 const parsers = require('./lib/serialport-parsers'); 8 const parsers = require('./lib/serialport-parsers');
9 const modemCommands = require('./lib/modem-commands'); 9 const modemCommands = require('./lib/modem-commands');
10 const modemInfo = require('./lib/modem-info'); 10 const modemInfo = require('./lib/modem-info');
11 11
12 const parserInterByteTimeout = new ParserInterByteTimeout({ interval: 1000 }); 12 const parserInterByteTimeout = new ParserInterByteTimeout({ interval: 1000 });
13 parserInterByteTimeout.on('data', (data) => { 13 parserInterByteTimeout.on('data', (data) => {
14 logger.verbose('INCOMING', { parser: 'parserInterByteTimeout', data: data.toString() }); 14 logger.verbose('INCOMING', { parser: 'parserInterByteTimeout', data: data.toString() });
15 }); 15 });
16 16
17 const port = new SerialPort(config.modem.device, { baudRate: 115200 }, async (err) => { 17 const port = new SerialPort(config.modem.device, { baudRate: 115200 }, async (err) => {
18 if (err) { 18 if (err) {
19 logger.warn(`Error opening modem. ${err}. Terminating modem ${config.modem.device}.`); 19 logger.warn(`Error opening modem. ${err}. Terminating modem ${config.modem.device}.`);
20 process.exit(1); 20 process.exit(1);
21 } 21 }
22 22
23 await modemCommands.sendCtrlZ(); 23 await modemCommands.sendCtrlZ();
24 // await modemCommands.writeToPortAndWaitForOkOrError('\rAT\r'); 24 // await modemCommands.writeToPortAndWaitForOkOrError('\rAT\r');
25 await modemCommands.writeToPortAndWaitForOkOrError('AT&FE0\r'); 25 await modemCommands.writeToPortAndWaitForOkOrError('AT&FE0\r');
26 await modemCommands.writeToPortAndWaitForOkOrError('AT+CMGF=0\r'); 26 await modemCommands.writeToPortAndWaitForOkOrError('AT+CMGF=0\r');
27 await modemCommands.writeToPortAndWaitForOkOrError('AT+CNMI=1,2,0,1,0\r'); 27 await modemCommands.writeToPortAndWaitForOkOrError('AT+CNMI=1,2,0,1,0\r');
28 28
29 await modemCommands.queryManufacturer(); 29 await modemCommands.queryManufacturer();
30 await modemCommands.queryModel(); 30 await modemCommands.queryModel();
31 31
32 await modemCommands.queryIMEIAndIMSI(); 32 await modemCommands.queryIMEIAndIMSI();
33 await modemCommands.queryCOPSAndSignalQuality(); 33 await modemCommands.queryCOPSAndSignalQuality();
34 logger.info('Modem state', modemInfo); 34 logger.info('Modem state', modemInfo);
35 35
36 // await modemCommands.sendSMS('628128364883', `coba pakai pdu ${new Date()}`); 36 // await modemCommands.sendSMS('628128364883', `coba pakai pdu ${new Date()}`);
37 // await modemCommands.sendSMS('+6282210008543', `coba pakai pdu ${new Date()}`); 37 // await modemCommands.sendSMS('+6282210008543', `coba pakai pdu ${new Date()}`);
38 // await modemCommands.sendSMS('6281809903333', `coba pakai pdu ${new Date()}`); 38 // await modemCommands.sendSMS('6281809903333', `coba pakai pdu ${new Date()}`);
39 await modemCommands.sendSMS('999', `coba pakai pdu ${new Date()}`);
40
39 // const ussdResponse = await modemCommands.executeUSSD('*888#', 2); 41 // const ussdResponse = await modemCommands.executeUSSD('*888#', 2);
40 // logger.info('USSD RESPONSE', { ussdResponse }); 42 // logger.info('USSD RESPONSE', { ussdResponse });
41 43
42 setInterval(async () => { 44 setInterval(async () => {
43 await modemCommands.initATCommands(); 45 await modemCommands.initATCommands();
44 await modemCommands.queryManufacturer(); 46 await modemCommands.queryManufacturer();
45 await modemCommands.queryModel(); 47 await modemCommands.queryModel();
46 await modemCommands.queryIMEIAndIMSI(); 48 await modemCommands.queryIMEIAndIMSI();
47 await modemCommands.queryCOPSAndSignalQuality(); 49 await modemCommands.queryCOPSAndSignalQuality();
48 logger.info('Modem state', modemInfo); 50 logger.info('Modem state', modemInfo);
49 }, config.interval_beetwen_signal_strength_ms || 30000); 51 }, config.interval_beetwen_signal_strength_ms || 30000);
50 }); 52 });
51 53
52 global.MODEM_PORT = port; 54 global.MODEM_PORT = port;
53 parsers.setPort(port); 55 parsers.setPort(port);
54 modemCommands.setPort(port); 56 modemCommands.setPort(port);
55 57
56 if (config && config.modem_tester && config.modem_tester.parser === 'regex') { 58 if (config && config.modem_tester && config.modem_tester.parser === 'regex') {
57 logger.info('Using parserWaitForOkOrError'); 59 logger.info('Using parserWaitForOkOrError');
58 port.pipe(parsers.parserWaitForOkOrError); 60 port.pipe(parsers.parserWaitForOkOrError);
59 } else if (config && config.modem_tester && config.modem_tester.parser === 'interbyte') { 61 } else if (config && config.modem_tester && config.modem_tester.parser === 'interbyte') {
60 logger.info('Using parserInterByteTimeout'); 62 logger.info('Using parserInterByteTimeout');
61 port.pipe(parserInterByteTimeout); 63 port.pipe(parserInterByteTimeout);
62 } else { 64 } else {
63 logger.info('Using parserReadline'); 65 logger.info('Using parserReadline');
64 port.pipe(parsers.parserReadline); 66 port.pipe(parsers.parserReadline);
65 } 67 }
66 68