Commit 3023245c4f75f2ffc9ab3fbb7b8d89012d165108
1 parent
edb8b6e81d
Exists in
master
Pengamanan tambahan
Showing 2 changed files with 3 additions and 1 deletions Inline Diff
lib/modem.js
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 SerialPort = require('serialport'); | 6 | const SerialPort = require('serialport'); |
7 | const ParserReadline = require('@serialport/parser-readline'); | 7 | const ParserReadline = require('@serialport/parser-readline'); |
8 | const ParserDelimiter = require('@serialport/parser-delimiter'); | 8 | const ParserDelimiter = require('@serialport/parser-delimiter'); |
9 | 9 | ||
10 | const config = require('komodo-sdk/config'); | 10 | const config = require('komodo-sdk/config'); |
11 | const logger = require('komodo-sdk/logger'); | 11 | const logger = require('komodo-sdk/logger'); |
12 | 12 | ||
13 | const mutex = require('./mutex'); | 13 | const mutex = require('./mutex'); |
14 | const common = require('./common'); | 14 | const common = require('./common'); |
15 | const sms = require('./sms'); | 15 | const sms = require('./sms'); |
16 | const dbCops = require('./db-cops'); | 16 | const dbCops = require('./db-cops'); |
17 | const reportSender = require('./report-sender'); | 17 | const reportSender = require('./report-sender'); |
18 | 18 | ||
19 | const modemInfo = { | 19 | const modemInfo = { |
20 | manufacturer: null, | 20 | manufacturer: null, |
21 | model: null, | 21 | model: null, |
22 | imei: null, | 22 | imei: null, |
23 | imsi: null, | 23 | imsi: null, |
24 | cops: null, | 24 | cops: null, |
25 | networkId: null, | 25 | networkId: null, |
26 | networkName: null, | 26 | networkName: null, |
27 | signalStrength: null, | 27 | signalStrength: null, |
28 | config: config.modem, | 28 | config: config.modem, |
29 | }; | 29 | }; |
30 | 30 | ||
31 | const port = new SerialPort(config.modem.device, { baudRate: 115200 }); | 31 | const port = new SerialPort(config.modem.device, { baudRate: 115200 }); |
32 | 32 | ||
33 | const parserReadLine = new ParserReadline(); | 33 | const parserReadLine = new ParserReadline(); |
34 | const parserWaitForOK = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK }); | 34 | const parserWaitForOK = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK }); |
35 | port.pipe(parserReadLine); | 35 | port.pipe(parserReadLine); |
36 | 36 | ||
37 | function writeToPort(data) { | 37 | function writeToPort(data) { |
38 | return new Promise((resolve) => { | 38 | return new Promise((resolve) => { |
39 | port.write(data, (err, bytesWritten) => { | 39 | port.write(data, (err, bytesWritten) => { |
40 | if (err) logger.warn(`ERROR: ${err.toString()}`); | 40 | if (err) logger.warn(`ERROR: ${err.toString()}`); |
41 | logger.verbose(`* OUT: ${data}`); | 41 | logger.verbose(`* OUT: ${data}`); |
42 | resolve(bytesWritten); | 42 | resolve(bytesWritten); |
43 | }); | 43 | }); |
44 | }); | 44 | }); |
45 | } | 45 | } |
46 | 46 | ||
47 | async function writeToPortAndWaitForOK(data) { | 47 | async function writeToPortAndWaitForOK(data) { |
48 | await mutex.setLockWaitForOK(); | 48 | await mutex.setLockWaitForOK(); |
49 | const result = await writeToPort(data); | 49 | const result = await writeToPort(data); |
50 | await mutex.setLockWaitForOK(); | 50 | await mutex.setLockWaitForOK(); |
51 | mutex.releaseLockWaitForOK(); | 51 | mutex.releaseLockWaitForOK(); |
52 | return result; | 52 | return result; |
53 | } | 53 | } |
54 | 54 | ||
55 | async function readSMS(slot) { | 55 | async function readSMS(slot) { |
56 | const parser = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK }); | 56 | const parser = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK }); |
57 | parser.on('data', async (data) => { | 57 | parser.on('data', async (data) => { |
58 | if (data) { | 58 | if (data) { |
59 | reportSender.incomingSMS(sms.extract(data.toString().trim())); | 59 | reportSender.incomingSMS(sms.extract(data.toString().trim())); |
60 | } | 60 | } |
61 | mutex.releaseLockWaitForOK(); | 61 | mutex.releaseLockWaitForOK(); |
62 | }); | 62 | }); |
63 | 63 | ||
64 | logger.info(`Reading SMS on slot ${slot}`); | 64 | logger.info(`Reading SMS on slot ${slot}`); |
65 | port.pipe(parser); | 65 | port.pipe(parser); |
66 | await writeToPortAndWaitForOK(`AT+CMGR=${slot}\r`); | 66 | await writeToPortAndWaitForOK(`AT+CMGR=${slot}\r`); |
67 | port.unpipe(parser); | 67 | port.unpipe(parser); |
68 | logger.verbose(`Finished reading SMS on slot ${slot}`); | 68 | logger.verbose(`Finished reading SMS on slot ${slot}`); |
69 | 69 | ||
70 | logger.info(`Deleting message on slot ${slot}`); | 70 | logger.info(`Deleting message on slot ${slot}`); |
71 | port.pipe(parserWaitForOK); | 71 | port.pipe(parserWaitForOK); |
72 | await writeToPortAndWaitForOK(`AT+CMGD=${slot}\r`); | 72 | await writeToPortAndWaitForOK(`AT+CMGD=${slot}\r`); |
73 | port.unpipe(parserWaitForOK); | 73 | port.unpipe(parserWaitForOK); |
74 | 74 | ||
75 | logger.info('Message processing has completed'); | 75 | logger.info('Message processing has completed'); |
76 | } | 76 | } |
77 | 77 | ||
78 | function onIncomingSMS(data) { | 78 | function onIncomingSMS(data) { |
79 | const value = common.extractValueFromReadLineData(data); | 79 | const value = common.extractValueFromReadLineData(data); |
80 | if (!value) return; | 80 | if (!value) return; |
81 | 81 | ||
82 | const chunks = value.split(','); | 82 | const chunks = value.split(','); |
83 | if (!chunks && !chunks[1]) return; | 83 | if (!chunks && !chunks[1]) return; |
84 | 84 | ||
85 | const slot = chunks[1]; | 85 | const slot = chunks[1]; |
86 | 86 | ||
87 | logger.info(`Incoming SMS on slot ${slot}`); | 87 | logger.info(`Incoming SMS on slot ${slot}`); |
88 | readSMS(slot); | 88 | readSMS(slot); |
89 | } | 89 | } |
90 | 90 | ||
91 | function onCOPS(data) { | 91 | function onCOPS(data) { |
92 | modemInfo.cops = common.extractValueFromReadLineData(data).trim(); | 92 | modemInfo.cops = common.extractValueFromReadLineData(data).trim(); |
93 | logger.info(`Connected Network: ${modemInfo.cops}`); | 93 | logger.info(`Connected Network: ${modemInfo.cops}`); |
94 | 94 | ||
95 | if (!modemInfo.cops) return; | 95 | if (!modemInfo.cops) return; |
96 | 96 | ||
97 | [, , modemInfo.networkId] = modemInfo.cops.split(','); | 97 | [, , modemInfo.networkId] = modemInfo.cops.split(','); |
98 | 98 | ||
99 | if (modemInfo.networkId) { | 99 | if (modemInfo.networkId) { |
100 | modemInfo.networkName = dbCops[modemInfo.networkId]; | 100 | modemInfo.networkName = dbCops[modemInfo.networkId]; |
101 | } | 101 | } |
102 | } | 102 | } |
103 | 103 | ||
104 | parserReadLine.on('data', (data) => { | 104 | parserReadLine.on('data', (data) => { |
105 | logger.verbose(`* IN: ${data}`); | 105 | logger.verbose(`* IN: ${data}`); |
106 | if (data) { | 106 | if (data) { |
107 | if (data.indexOf('+CSQ: ') === 0) { | 107 | if (data.indexOf('+CSQ: ') === 0) { |
108 | modemInfo.signalStrength = common.extractValueFromReadLineData(data).trim(); | 108 | modemInfo.signalStrength = common.extractValueFromReadLineData(data).trim(); |
109 | logger.info(`Signal strength: ${modemInfo.signalStrength}`); | 109 | logger.info(`Signal strength: ${modemInfo.signalStrength}`); |
110 | } else if (data.indexOf('+CMTI: ') === 0) { | 110 | } else if (data.indexOf('+CMTI: ') === 0) { |
111 | onIncomingSMS(data); | 111 | onIncomingSMS(data); |
112 | } else if (data.indexOf('+COPS: ') === 0) { | 112 | } else if (data.indexOf('+COPS: ') === 0) { |
113 | onCOPS(data); | 113 | onCOPS(data); |
114 | } | 114 | } |
115 | } | 115 | } |
116 | }); | 116 | }); |
117 | 117 | ||
118 | parserWaitForOK.on('data', () => { | 118 | parserWaitForOK.on('data', () => { |
119 | mutex.releaseLockWaitForOK(); | 119 | mutex.releaseLockWaitForOK(); |
120 | }); | 120 | }); |
121 | 121 | ||
122 | function simpleCommand(cmd, callback) { | 122 | function simpleCommand(cmd, callback) { |
123 | const parser = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK }); | 123 | const parser = new ParserDelimiter({ delimiter: DELIMITER_WAIT_FOR_OK }); |
124 | parser.on('data', (data) => { | 124 | parser.on('data', (data) => { |
125 | port.unpipe(parser); | 125 | port.unpipe(parser); |
126 | mutex.releaseLockWaitForOK(); | 126 | mutex.releaseLockWaitForOK(); |
127 | 127 | ||
128 | if (data) { | 128 | if (data) { |
129 | callback(null, data.toString().trim()); | 129 | callback(null, data.toString().trim()); |
130 | } | 130 | } |
131 | }); | 131 | }); |
132 | 132 | ||
133 | port.pipe(parser); | 133 | port.pipe(parser); |
134 | writeToPortAndWaitForOK(cmd); | 134 | writeToPortAndWaitForOK(cmd); |
135 | } | 135 | } |
136 | 136 | ||
137 | function readManufacturer() { | 137 | function readManufacturer() { |
138 | return new Promise((resolve) => { | 138 | return new Promise((resolve) => { |
139 | simpleCommand('AT+CGMI\r', (err, result) => { | 139 | simpleCommand('AT+CGMI\r', (err, result) => { |
140 | modemInfo.manufacturer = result; | 140 | modemInfo.manufacturer = result; |
141 | logger.info(`Manufacturer: ${result}`); | 141 | logger.info(`Manufacturer: ${result}`); |
142 | resolve(result); | 142 | resolve(result); |
143 | }); | 143 | }); |
144 | }); | 144 | }); |
145 | } | 145 | } |
146 | 146 | ||
147 | function readModel() { | 147 | function readModel() { |
148 | return new Promise((resolve) => { | 148 | return new Promise((resolve) => { |
149 | simpleCommand('AT+CGMM\r', (err, result) => { | 149 | simpleCommand('AT+CGMM\r', (err, result) => { |
150 | modemInfo.model = result; | 150 | modemInfo.model = result; |
151 | logger.info(`Model: ${result}`); | 151 | logger.info(`Model: ${result}`); |
152 | resolve(result); | 152 | resolve(result); |
153 | }); | 153 | }); |
154 | }); | 154 | }); |
155 | } | 155 | } |
156 | 156 | ||
157 | function readIMEI() { | 157 | function readIMEI() { |
158 | return new Promise((resolve) => { | 158 | return new Promise((resolve) => { |
159 | simpleCommand('AT+CGSN\r', (err, result) => { | 159 | simpleCommand('AT+CGSN\r', (err, result) => { |
160 | modemInfo.imei = result; | 160 | modemInfo.imei = result; |
161 | logger.info(`IMEI: ${result}`); | 161 | logger.info(`IMEI: ${result}`); |
162 | resolve(result); | 162 | resolve(result); |
163 | }); | 163 | }); |
164 | }); | 164 | }); |
165 | } | 165 | } |
166 | 166 | ||
167 | function readIMSI() { | 167 | function readIMSI() { |
168 | return new Promise((resolve) => { | 168 | return new Promise((resolve) => { |
169 | simpleCommand('AT+CIMI\r', (err, result) => { | 169 | simpleCommand('AT+CIMI\r', (err, result) => { |
170 | modemInfo.imsi = result; | 170 | modemInfo.imsi = result; |
171 | logger.info(`IMSI: ${result}`); | 171 | logger.info(`IMSI: ${result}`); |
172 | resolve(result); | 172 | resolve(result); |
173 | }); | 173 | }); |
174 | }); | 174 | }); |
175 | } | 175 | } |
176 | 176 | ||
177 | async function querySignalStrength() { | 177 | async function querySignalStrength() { |
178 | port.pipe(parserWaitForOK); | 178 | port.pipe(parserWaitForOK); |
179 | await writeToPortAndWaitForOK('AT+CSQ\r'); | 179 | await writeToPortAndWaitForOK('AT+CSQ\r'); |
180 | port.unpipe(parserWaitForOK); | 180 | port.unpipe(parserWaitForOK); |
181 | } | 181 | } |
182 | 182 | ||
183 | async function registerSignalStrengthBackgroundQuery() { | 183 | async function registerSignalStrengthBackgroundQuery() { |
184 | logger.info('Registering background signal strength query'); | 184 | logger.info('Registering background signal strength query'); |
185 | 185 | ||
186 | setInterval(() => { | 186 | setInterval(() => { |
187 | querySignalStrength(); | 187 | querySignalStrength(); |
188 | }, INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS); | 188 | }, INTERVAL_BEETWEN_SIGNAL_STRENGTH_MS); |
189 | } | 189 | } |
190 | 190 | ||
191 | async function sendSMS(destination, msg) { | 191 | async function sendSMS(destination, msg) { |
192 | if (typeof destination !== 'string' || typeof msg !== 'string' || !destination.trim() || !msg.trim()) return; | ||
193 | |||
192 | await mutex.setLockWaitForCommand(); | 194 | await mutex.setLockWaitForCommand(); |
193 | logger.info('Sending message', { destination, msg }); | 195 | logger.info('Sending message', { destination, msg }); |
194 | 196 | ||
195 | const correctedDestination = `+${destination}`.replace(/^0/, '62').replace(/^\++/, '+'); | 197 | const correctedDestination = `+${destination}`.replace(/^0/, '62').replace(/^\++/, '+'); |
196 | 198 | ||
197 | port.pipe(parserWaitForOK); | 199 | port.pipe(parserWaitForOK); |
198 | await writeToPortAndWaitForOK('AT+CMGF=1\r'); | 200 | await writeToPortAndWaitForOK('AT+CMGF=1\r'); |
199 | await writeToPortAndWaitForOK(`AT+CMGS="${correctedDestination}"\n${msg}${Buffer.from([0x1A])}`); | 201 | await writeToPortAndWaitForOK(`AT+CMGS="${correctedDestination}"\n${msg}${Buffer.from([0x1A])}`); |
200 | port.unpipe(parserWaitForOK); | 202 | port.unpipe(parserWaitForOK); |
201 | 203 | ||
202 | logger.info('Message has been sent'); | 204 | logger.info('Message has been sent'); |
203 | 205 | ||
204 | mutex.releaseLockWaitForCommand(); | 206 | mutex.releaseLockWaitForCommand(); |
205 | } | 207 | } |
206 | 208 | ||
207 | function init() { | 209 | function init() { |
208 | port.on('open', async () => { | 210 | port.on('open', async () => { |
209 | port.pipe(parserWaitForOK); | 211 | port.pipe(parserWaitForOK); |
210 | 212 | ||
211 | logger.info('Modem opened'); | 213 | logger.info('Modem opened'); |
212 | await writeToPortAndWaitForOK('AT\r'); | 214 | await writeToPortAndWaitForOK('AT\r'); |
213 | 215 | ||
214 | logger.info('Initializing modem to factory set'); | 216 | logger.info('Initializing modem to factory set'); |
215 | await writeToPortAndWaitForOK('AT&F\r'); | 217 | await writeToPortAndWaitForOK('AT&F\r'); |
216 | 218 | ||
217 | logger.info('Disabling echo'); | 219 | logger.info('Disabling echo'); |
218 | await writeToPortAndWaitForOK('ATE0\r'); | 220 | await writeToPortAndWaitForOK('ATE0\r'); |
219 | 221 | ||
220 | await writeToPortAndWaitForOK('AT+COPS?\r'); | 222 | await writeToPortAndWaitForOK('AT+COPS?\r'); |
221 | 223 | ||
222 | logger.info('Querying signal strength'); | 224 | logger.info('Querying signal strength'); |
223 | await writeToPortAndWaitForOK('AT+CSQ\r'); | 225 | await writeToPortAndWaitForOK('AT+CSQ\r'); |
224 | 226 | ||
225 | logger.info('Deleting existing messages'); | 227 | logger.info('Deleting existing messages'); |
226 | // await writeToPortAndWaitForOK('AT+CMGD=0,4\r'); | 228 | // await writeToPortAndWaitForOK('AT+CMGD=0,4\r'); |
227 | 229 | ||
228 | await readManufacturer(); | 230 | await readManufacturer(); |
229 | await readModel(); | 231 | await readModel(); |
230 | await readIMEI(); | 232 | await readIMEI(); |
231 | await readIMSI(); | 233 | await readIMSI(); |
232 | 234 | ||
233 | port.unpipe(parserWaitForOK); | 235 | port.unpipe(parserWaitForOK); |
234 | 236 | ||
235 | registerSignalStrengthBackgroundQuery(); | 237 | registerSignalStrengthBackgroundQuery(); |
236 | logger.verbose('Init completed'); | 238 | logger.verbose('Init completed'); |
237 | }); | 239 | }); |
238 | } | 240 | } |
239 | 241 | ||
240 | init(); | 242 | init(); |
241 | 243 | ||
242 | exports.modemInfo = modemInfo; | 244 | exports.modemInfo = modemInfo; |
243 | exports.sendSMS = sendSMS; | 245 | exports.sendSMS = sendSMS; |
244 | 246 |
lib/mutex.js
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 | function setLockWaitForOK() { | 8 | function setLockWaitForOK() { |
9 | return new Promise((resolve) => { | 9 | return new Promise((resolve) => { |
10 | mutexWaitForOK.lock(() => { | 10 | mutexWaitForOK.lock(() => { |
11 | resolve(true); | 11 | resolve(true); |
12 | }); | 12 | }); |
13 | }); | 13 | }); |
14 | } | 14 | } |
15 | 15 | ||
16 | function releaseLockWaitForOK() { | 16 | function releaseLockWaitForOK() { |
17 | try { | 17 | try { |
18 | mutexWaitForOK.unlock(); | 18 | mutexWaitForOK.unlock(); |
19 | } catch (e) { | 19 | } catch (e) { |
20 | // | 20 | // |
21 | } | 21 | } |
22 | } | 22 | } |
23 | 23 | ||
24 | function setLockWaitForCommand() { | 24 | function setLockWaitForCommand() { |
25 | return new Promise((resolve) => { | 25 | return new Promise((resolve) => { |
26 | mutexCommand.lock(() => { | 26 | mutexCommand.lock(() => { |
27 | resolve(true); | 27 | resolve(true); |
28 | }); | 28 | }); |
29 | }); | 29 | }); |
30 | } | 30 | } |
31 | 31 | ||
32 | function releaseLockWaitForCommand() { | 32 | function releaseLockWaitForCommand() { |
33 | setTimeout(() => { | 33 | setTimeout(() => { |
34 | try { | 34 | try { |
35 | mutexCommand.unlock(); | 35 | mutexCommand.unlock(); |
36 | } catch (e) { | 36 | } catch (e) { |
37 | // | 37 | // |
38 | } | 38 | } |
39 | }, 1500); | 39 | }, 2000); |
40 | } | 40 | } |
41 | 41 | ||
42 | exports.setLockWaitForOK = setLockWaitForOK; | 42 | exports.setLockWaitForOK = setLockWaitForOK; |
43 | exports.releaseLockWaitForOK = releaseLockWaitForOK; | 43 | exports.releaseLockWaitForOK = releaseLockWaitForOK; |
44 | 44 | ||
45 | exports.setLockWaitForCommand = setLockWaitForCommand; | 45 | exports.setLockWaitForCommand = setLockWaitForCommand; |
46 | exports.releaseLockWaitForCommand = releaseLockWaitForCommand; | 46 | exports.releaseLockWaitForCommand = releaseLockWaitForCommand; |
47 | 47 |