Commit 0bdac2f9c68646872fc3ed0676950c56ff8468af

Authored by Adhidarma Hadiwinoto
1 parent 26921e0c2d
Exists in master

Bersih kirim sms banyak sekaligus tdk tabrakan dengan CSQ

Showing 3 changed files with 107 additions and 38 deletions Inline Diff

1 { 1 {
2 "name": "SMS0", 2 "name": "SMS0",
3 "modem": { 3 "modem": {
4 "device": "/dev/ttyUSB0", 4 "device": "/dev/ttyUSB0",
5 "options": { 5 "options": {
6 "baudRate": 115200 6 "baudRate": 115200
7 } 7 }
8 }, 8 },
9 "report_url": { 9 "report_url": {
10 "incoming_sms": "http://localhost:16481/apikey/PLEASE_CHANGE_ME/on-sms" 10 "incoming_sms": "http://localhost:16481/apikey/PLEASE_CHANGE_ME/on-sms"
11 }, 11 },
12 "http_command_server": { 12 "http_command_server": {
13 "apikey": "PLEASE_CHANGE_ME", 13 "apikey": "PLEASE_CHANGE_ME",
14 "listen_port": "2110" 14 "listen_port": "2110"
15 }, 15 },
16 16
17 "interval_beetwen_signal_strength_ms": 60000,
17 "disable_delete_inbox_on_startup": false 18 "disable_delete_inbox_on_startup": false
18 19
19 } 20 }
1 'use strict'; 1 'use strict';
2 2
3 const INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS = 60000; 3 const INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS = 60000;
4 const DELIMITER_WAIT_FOR_OK = '\nOK\r\n'; 4 const DELIMITER_WAIT_FOR_OK = '\nOK\r\n';
5 5
6 const moment = require('moment'); 6 const moment = require('moment');
7 const SerialPort = require('serialport'); 7 const SerialPort = require('serialport');
8 const ParserReadline = require('@serialport/parser-readline'); 8 const ParserReadline = require('@serialport/parser-readline');
9 const ParserDelimiter = require('@serialport/parser-delimiter'); 9 const ParserDelimiter = require('@serialport/parser-delimiter');
10 10
11 const config = require('komodo-sdk/config'); 11 const config = require('komodo-sdk/config');
12 const logger = require('komodo-sdk/logger'); 12 const logger = require('komodo-sdk/logger');
13 13
14 const mutex = require('./mutex'); 14 const mutex = require('./mutex');
15 const common = require('./common'); 15 const common = require('./common');
16 const sms = require('./sms'); 16 const sms = require('./sms');
17 const dbCops = require('./db-cops'); 17 const dbCops = require('./db-cops');
18 const reportSender = require('./report-sender'); 18 const reportSender = require('./report-sender');
19 const msisdn = require('./msisdn'); 19 const msisdn = require('./msisdn');
20 20
21 const modemInfo = { 21 const modemInfo = {
22 manufacturer: null, 22 manufacturer: null,
23 model: null, 23 model: null,
24 imei: null, 24 imei: null,
25 imsi: null, 25 imsi: null,
26 msisdn: null, 26 msisdn: null,
27 cops: null, 27 cops: null,
28 networkId: null, 28 networkId: null,
29 networkName: null, 29 networkName: null,
30 signalStrength: null, 30 signalStrength: null,
31 signalStrengthTs: null, 31 signalStrengthTs: null,
32 signalStrengthTsReadable: null, 32 signalStrengthTsReadable: null,
33 config: config.modem, 33 config: config.modem,
34 }; 34 };
35 35
36 const port = new SerialPort(config.modem.device, { baudRate: 115200 }); 36 const port = new SerialPort(config.modem.device, { baudRate: 115200 });
37 37
38 const parserReadLine = new ParserReadline(); 38 const parserReadLine = new ParserReadline();
39
39 const parserWaitForOK = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK }); 40 const parserWaitForOK = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK });
41 parserWaitForOK.on('data', () => {
42 mutex.releaseLockWaitForCommand();
43 });
44
45
40 port.pipe(parserReadLine); 46 port.pipe(parserReadLine);
41 47
42 function writeToPort(data) { 48 function writeToPort(data) {
43 return new Promise((resolve) => { 49 return new Promise((resolve) => {
44 port.write(data, (err, bytesWritten) => { 50 port.write(data, (err, bytesWritten) => {
45 if (err) logger.warn(`ERROR: ${err.toString()}`); 51 if (err) logger.warn(`ERROR: ${err.toString()}`);
46 logger.verbose(`* OUT: ${data}`); 52 logger.verbose(`* OUT: ${data}`);
47 resolve(bytesWritten); 53 resolve(bytesWritten);
48 }); 54 });
49 }); 55 });
50 } 56 }
51 57
52 async function writeToPortAndWaitForOK(data) { 58 async function writeToPortAndWaitForOK(data) {
53 await mutex.setLockWaitForOK(); 59 await mutex.setLockWaitForCommand();
54 const result = await writeToPort(data); 60 const result = await writeToPort(data);
55 await mutex.setLockWaitForOK(); 61
56 mutex.releaseLockWaitForOK(); 62 await mutex.setLockWaitForCommand();
63 mutex.releaseLockWaitForCommand();
64
57 return result; 65 return result;
58 } 66 }
59 67
60 async function readSMS(slot) { 68 async function readSMS(slot) {
61 const parser = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK }); 69 const parserCMGR = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK });
62 parser.on('data', async (data) => { 70 parserCMGR.on('data', (data) => {
63 if (data) { 71 if (data) {
64 reportSender.incomingSMS(sms.extract(data.toString().trim())); 72 reportSender.incomingSMS(sms.extract(data.toString().trim()));
65 } 73 }
66 mutex.releaseLockWaitForOK(); 74 port.unpipe(parserCMGR);
75 mutex.releaseLockWaitForCommand();
76 });
77
78 const parserCMGD = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK });
79 parserCMGD.on('data', () => {
80 port.unpipe(parserCMGD);
81 mutex.releaseLockWaitForCommand();
67 }); 82 });
68 83
69 logger.info(`Reading SMS on slot ${slot}`); 84 logger.info(`Reading SMS on slot ${slot}`);
70 port.pipe(parser); 85 await mutex.setLockWaitForCommand();
71 await writeToPortAndWaitForOK(`AT+CMGR=${slot}\r`); 86 port.pipe(parserCMGR);
72 port.unpipe(parser); 87 await writeToPort(`AT+CMGR=${slot}\r`);
73 logger.verbose(`Finished reading SMS on slot ${slot}`); 88 logger.info(`Finished reading SMS on slot ${slot}`);
74 89
75 logger.info(`Deleting message on slot ${slot}`); 90 logger.info(`Deleting message on slot ${slot}`);
76 port.pipe(parserWaitForOK); 91 await mutex.setLockWaitForCommand();
77 await writeToPortAndWaitForOK(`AT+CMGD=${slot}\r`); 92 port.pipe(parserCMGD);
78 port.unpipe(parserWaitForOK); 93 await writeToPort(`AT+CMGD=${slot}\r`);
79
80 logger.info('Message processing has completed'); 94 logger.info('Message processing has completed');
81 } 95 }
82 96
83 function onIncomingSMS(data) { 97 function onIncomingSMS(data) {
84 const value = common.extractValueFromReadLineData(data); 98 const value = common.extractValueFromReadLineData(data);
85 if (!value) return; 99 if (!value) return;
86 100
87 const chunks = value.split(','); 101 const chunks = value.split(',');
88 if (!chunks && !chunks[1]) return; 102 if (!chunks && !chunks[1]) return;
89 103
90 const slot = chunks[1]; 104 const slot = chunks[1];
91 105
92 logger.info(`Incoming SMS on slot ${slot}`); 106 logger.info(`Incoming SMS on slot ${slot}`);
93 readSMS(slot); 107 readSMS(slot);
94 } 108 }
95 109
96 function onCOPS(data) { 110 function onCOPS(data) {
97 modemInfo.cops = common.extractValueFromReadLineData(data).trim(); 111 modemInfo.cops = common.extractValueFromReadLineData(data).trim();
98 logger.info(`Connected Network: ${modemInfo.cops}`); 112 logger.info(`Connected Network: ${modemInfo.cops}`);
99 113
100 if (!modemInfo.cops) return; 114 if (!modemInfo.cops) return;
101 115
102 [, , modemInfo.networkId] = modemInfo.cops.split(','); 116 [, , modemInfo.networkId] = modemInfo.cops.split(',');
103 117
104 if (modemInfo.networkId) { 118 if (modemInfo.networkId) {
105 modemInfo.networkName = dbCops[modemInfo.networkId]; 119 modemInfo.networkName = dbCops[modemInfo.networkId];
106 } 120 }
107 } 121 }
108 122
109 parserReadLine.on('data', (data) => { 123 parserReadLine.on('data', (data) => {
110 logger.verbose(`* IN: ${data}`); 124 logger.verbose(`* IN: ${data}`);
111 if (data) { 125 if (data) {
112 if (data.indexOf('+CSQ: ') === 0) { 126 if (data.indexOf('+CSQ: ') === 0) {
113 const signalStrength = common.extractValueFromReadLineData(data).trim(); 127 const signalStrength = common.extractValueFromReadLineData(data).trim();
114 if (signalStrength) { 128 if (signalStrength) {
115 modemInfo.signalStrength = signalStrength; 129 modemInfo.signalStrength = signalStrength;
116 modemInfo.signalStrengthTs = new Date(); 130 modemInfo.signalStrengthTs = new Date();
117 modemInfo.signalStrengthTsReadable = moment(modemInfo.signalStrengthTs).format('YYYY-MM-DD HH:mm:ss'); 131 modemInfo.signalStrengthTsReadable = moment(modemInfo.signalStrengthTs).format('YYYY-MM-DD HH:mm:ss');
118 logger.info(`Signal strength: ${modemInfo.signalStrength}`); 132 logger.info(`Signal strength: ${modemInfo.signalStrength}`);
119 } 133 }
120 } else if (data.indexOf('+CMTI: ') === 0) { 134 } else if (data.indexOf('+CMTI: ') === 0) {
121 onIncomingSMS(data); 135 onIncomingSMS(data);
122 } else if (data.indexOf('+COPS: ') === 0) { 136 } else if (data.indexOf('+COPS: ') === 0) {
123 onCOPS(data); 137 onCOPS(data);
124 } 138 }
125 } 139 }
126 }); 140 });
127 141
128 parserWaitForOK.on('data', () => {
129 mutex.releaseLockWaitForOK();
130 });
131
132 function simpleCommand(cmd, callback) { 142 function simpleCommand(cmd, callback) {
133 const parser = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK }); 143 const parser = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK });
134 parser.on('data', (data) => { 144 parser.on('data', (data) => {
135 port.unpipe(parser); 145 port.unpipe(parser);
136 mutex.releaseLockWaitForOK(); 146 mutex.releaseLockWaitForCommand();
137 147
138 if (data) { 148 if (data) {
139 callback(null, data.toString().trim()); 149 callback(null, data.toString().trim());
140 } 150 }
141 }); 151 });
142 152
143 port.pipe(parser); 153 port.pipe(parser);
144 writeToPortAndWaitForOK(cmd); 154 writeToPortAndWaitForOK(cmd);
145 } 155 }
146 156
147 function readManufacturer() { 157 function readManufacturer() {
148 return new Promise((resolve) => { 158 return new Promise((resolve) => {
149 simpleCommand('AT+CGMI\r', (err, result) => { 159 simpleCommand('AT+CGMI\r', (err, result) => {
150 modemInfo.manufacturer = result; 160 modemInfo.manufacturer = result;
151 logger.info(`Manufacturer: ${result}`); 161 logger.info(`Manufacturer: ${result}`);
152 resolve(result); 162 resolve(result);
153 }); 163 });
154 }); 164 });
155 } 165 }
156 166
157 function readModel() { 167 function readModel() {
158 return new Promise((resolve) => { 168 return new Promise((resolve) => {
159 simpleCommand('AT+CGMM\r', (err, result) => { 169 simpleCommand('AT+CGMM\r', (err, result) => {
160 modemInfo.model = result; 170 modemInfo.model = result;
161 logger.info(`Model: ${result}`); 171 logger.info(`Model: ${result}`);
162 resolve(result); 172 resolve(result);
163 }); 173 });
164 }); 174 });
165 } 175 }
166 176
167 function readIMEI() { 177 function readIMEI() {
168 return new Promise((resolve) => { 178 return new Promise((resolve) => {
169 simpleCommand('AT+CGSN\r', (err, result) => { 179 simpleCommand('AT+CGSN\r', (err, result) => {
170 modemInfo.imei = result; 180 modemInfo.imei = result;
171 logger.info(`IMEI: ${result}`); 181 logger.info(`IMEI: ${result}`);
172 resolve(result); 182 resolve(result);
173 }); 183 });
174 }); 184 });
175 } 185 }
176 186
177 function readIMSI() { 187 function readIMSI() {
178 return new Promise((resolve) => { 188 return new Promise((resolve) => {
179 simpleCommand('AT+CIMI\r', (err, result) => { 189 simpleCommand('AT+CIMI\r', (err, result) => {
180 modemInfo.imsi = result; 190 modemInfo.imsi = result;
181 logger.info(`IMSI: ${result}`); 191 logger.info(`IMSI: ${result}`);
182 192
183 if (result) { 193 if (result) {
184 modemInfo.msisdn = msisdn[result]; 194 modemInfo.msisdn = msisdn[result];
185 if (modemInfo.msisdn) { 195 if (modemInfo.msisdn) {
186 logger.info(`MSISDN: ${modemInfo.msisdn}`); 196 logger.info(`MSISDN: ${modemInfo.msisdn}`);
187 } 197 }
188 } 198 }
189 resolve(result); 199 resolve(result);
190 }); 200 });
191 }); 201 });
192 } 202 }
193 203
194 async function querySignalStrength() { 204 async function querySignalStrength() {
195 port.pipe(parserWaitForOK); 205 const parser = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK });
196 await writeToPortAndWaitForOK('AT+CSQ\r'); 206 parser.on('data', () => {
197 port.unpipe(parserWaitForOK); 207 port.unpipe(parser);
208 mutex.releaseLockWaitForCommand();
209 });
210
211 if (mutex.tryLockWaitForCommand()) {
212 port.pipe(parser);
213 await writeToPort('AT+CSQ\r');
214 }
198 } 215 }
199 216
200 async function registerSignalStrengthBackgroundQuery() { 217 async function registerSignalStrengthBackgroundQuery() {
201 logger.info('Registering background signal strength query'); 218 logger.info('Registering background signal strength query');
202 219
220 querySignalStrength();
221
203 setInterval(() => { 222 setInterval(() => {
204 querySignalStrength(); 223 querySignalStrength();
205 }, INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS); 224 }, config.interval_beetwen_signal_strength_ms || INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS);
206 } 225 }
207 226
208 async function sendSMS(destination, msg) { 227 async function sendSMS(destination, msg) {
209 if (typeof destination !== 'string' || typeof msg !== 'string' || !destination.trim() || !msg.trim()) return; 228 if (typeof destination !== 'string' || typeof msg !== 'string' || !destination.trim() || !msg.trim()) return;
210 229
230 const parser = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK });
231 parser.on('data', () => {
232 port.unpipe(parser);
233 mutex.releaseLockWaitForSubCommand();
234 });
235
236 logger.verbose('Waiting for command lock to send message');
211 await mutex.setLockWaitForCommand(); 237 await mutex.setLockWaitForCommand();
238
212 logger.info('Sending message', { destination, msg }); 239 logger.info('Sending message', { destination, msg });
213 240
214 const correctedDestination = `+${destination}`.replace(/^0/, '62').replace(/^\++/, '+'); 241 const correctedDestination = `+${destination}`.replace(/^0/, '62').replace(/^\++/, '+');
215 242
216 port.pipe(parserWaitForOK); 243 logger.verbose('Waiting for lock before set to text mode');
217 await writeToPortAndWaitForOK('AT+CMGF=1\r'); 244 await mutex.setLockWaitForSubCommand();
218 await writeToPortAndWaitForOK(`AT+CMGS="${correctedDestination}"\n${msg}${Buffer.from([0x1A])}`); 245 port.pipe(parser);
219 port.unpipe(parserWaitForOK); 246 await writeToPort('AT+CMGF=1\r');
247
248 logger.verbose('Waiting for lock before writing message');
249 await mutex.setLockWaitForSubCommand();
250 port.pipe(parser);
251 await writeToPort(`AT+CMGS="${correctedDestination}"\n${msg}${Buffer.from([0x1A])}`);
252
253 await mutex.setLockWaitForSubCommand();
254 mutex.releaseLockWaitForSubCommand();
220 255
221 logger.info('Message has been sent'); 256 logger.info('Message has been sent');
222 257
223 mutex.releaseLockWaitForCommand(); 258 setTimeout(() => {
259 logger.verbose('Releasing command lock');
260 mutex.releaseLockWaitForCommand();
261 }, 2000);
224 } 262 }
225 263
226 function init() { 264 function init() {
227 port.on('open', async () => { 265 port.on('open', async () => {
266 // await mutex.setLockWaitForCommand();
228 port.pipe(parserWaitForOK); 267 port.pipe(parserWaitForOK);
229 268
230 logger.info('Modem opened'); 269 logger.info('Modem opened');
231 await writeToPortAndWaitForOK('AT\r'); 270 await writeToPortAndWaitForOK('AT\r');
232 271
233 logger.info('Initializing modem to factory set'); 272 logger.info('Initializing modem to factory set');
234 await writeToPortAndWaitForOK('AT&F\r'); 273 await writeToPortAndWaitForOK('AT&F\r');
235 274
236 logger.info('Disabling echo'); 275 logger.info('Disabling echo');
237 await writeToPortAndWaitForOK('ATE0\r'); 276 await writeToPortAndWaitForOK('ATE0\r');
238 277
239 await writeToPortAndWaitForOK('AT+COPS?\r'); 278 await writeToPortAndWaitForOK('AT+COPS?\r');
240 279
241 logger.info('Querying signal strength');
242 await writeToPortAndWaitForOK('AT+CSQ\r');
243
244 await readManufacturer(); 280 await readManufacturer();
245 await readModel(); 281 await readModel();
246 await readIMEI(); 282 await readIMEI();
247 await readIMSI(); 283 await readIMSI();
248 284
249 if (!config.disable_delete_inbox_on_startup) { 285 if (!config.disable_delete_inbox_on_startup) {
250 logger.info('Deleting existing messages'); 286 logger.info('Deleting existing messages');
251 await writeToPortAndWaitForOK('AT+CMGD=0,4\r'); 287 await writeToPortAndWaitForOK('AT+CMGD=0,4\r');
252 } 288 }
253 289
254 port.unpipe(parserWaitForOK); 290 port.unpipe(parserWaitForOK);
255 291
292 await mutex.setLockWaitForCommand();
293 mutex.releaseLockWaitForCommand();
294
256 registerSignalStrengthBackgroundQuery(); 295 registerSignalStrengthBackgroundQuery();
257 logger.verbose('Init completed'); 296 logger.verbose('Init completed');
1 'use strict'; 1 'use strict';
2 2
3 const locks = require('locks'); 3 const locks = require('locks');
4 4
5 const mutexWaitForOK = locks.createMutex(); 5 const mutexWaitForOK = locks.createMutex();
6 const mutexCommand = locks.createMutex(); 6 const mutexCommand = locks.createMutex();
7 7
8 const mutexSubCommand = locks.createMutex();
9
8 function setLockWaitForOK() { 10 function setLockWaitForOK() {
9 return new Promise((resolve) => { 11 return new Promise((resolve) => {
10 mutexWaitForOK.lock(() => { 12 mutexWaitForOK.lock(() => {
11 resolve(true); 13 resolve(true);
12 }); 14 });
13 }); 15 });
14 } 16 }
15 17
16 function releaseLockWaitForOK() { 18 function releaseLockWaitForOK() {
17 try { 19 try {
18 mutexWaitForOK.unlock(); 20 mutexWaitForOK.unlock();
19 } catch (e) { 21 } catch (e) {
20 // 22 //
21 } 23 }
22 } 24 }
23 25
24 function setLockWaitForCommand() { 26 function setLockWaitForCommand() {
25 return new Promise((resolve) => { 27 return new Promise((resolve) => {
26 mutexCommand.lock(() => { 28 mutexCommand.lock(() => {
27 resolve(true); 29 resolve(true);
28 }); 30 });
29 }); 31 });
30 } 32 }
31 33
34 function tryLockWaitForCommand() {
35 return mutexCommand.tryLock();
36 }
37
38
32 function releaseLockWaitForCommand() { 39 function releaseLockWaitForCommand() {
33 setTimeout(() => { 40 try {
34 try { 41 mutexCommand.unlock();
35 mutexCommand.unlock(); 42 } catch (e) {
36 } catch (e) { 43 //
37 // 44 }
38 } 45 }
39 }, 2000); 46
47 function setLockWaitForSubCommand() {
48 return new Promise((resolve) => {
49 mutexSubCommand.lock(() => {
50 resolve(true);
51 });
52 });
53 }
54
55 function releaseLockWaitForSubCommand() {
56 mutexSubCommand.unlock();
57
58 /*
59 try {
60 mutexSubCommand.unlock();
61 } catch (e) {
62 //
63 }
64 */
40 } 65 }
41 66
42 exports.setLockWaitForOK = setLockWaitForOK; 67 exports.setLockWaitForOK = setLockWaitForOK;
43 exports.releaseLockWaitForOK = releaseLockWaitForOK; 68 exports.releaseLockWaitForOK = releaseLockWaitForOK;
44 69
45 exports.setLockWaitForCommand = setLockWaitForCommand; 70 exports.setLockWaitForCommand = setLockWaitForCommand;
46 exports.releaseLockWaitForCommand = releaseLockWaitForCommand; 71 exports.releaseLockWaitForCommand = releaseLockWaitForCommand;
72 exports.tryLockWaitForCommand = tryLockWaitForCommand;
73
74 exports.setLockWaitForSubCommand = setLockWaitForSubCommand;
75 exports.releaseLockWaitForSubCommand = releaseLockWaitForSubCommand;
47 76