Fork of my original MQTTGateway

Dependencies:   mbed-http

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ATParser.cpp Source File

ATParser.cpp

00001 /* Copyright (c) 2015 ARM Limited
00002  *
00003  * Licensed under the Apache License, Version 2.0 (the "License");
00004  * you may not use this file except in compliance with the License.
00005  * You may obtain a copy of the License at
00006  *
00007  *     http://www.apache.org/licenses/LICENSE-2.0
00008  *
00009  * Unless required by applicable law or agreed to in writing, software
00010  * distributed under the License is distributed on an "AS IS" BASIS,
00011  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012  * See the License for the specific language governing permissions and
00013  * limitations under the License.
00014  *
00015  * @section DESCRIPTION
00016  *
00017  * Parser for the AT command syntax
00018  *
00019  */
00020 
00021 #include "ATParser.h"
00022 #include "mbed_debug.h"
00023 
00024 
00025 // getc/putc handling with timeouts
00026 int ATParser::putc(char c)
00027 {
00028     Timer timer;
00029     timer.start();
00030 
00031     while (true) {
00032         if (_serial->writeable()) {
00033             return _serial->putc(c);
00034         }
00035         if (timer.read_ms() > _timeout) {
00036             return -1;
00037         }
00038     }
00039 }
00040 
00041 int ATParser::getc()
00042 {
00043     Timer timer;
00044     timer.start();
00045 
00046     while (true) {
00047         if (_serial->readable()) {
00048             return _serial->getc();
00049         }
00050         if (timer.read_ms() > _timeout) {
00051             return -1;
00052         }
00053     }
00054 }
00055 
00056 void ATParser::flush()
00057 {
00058     while (_serial->readable()) {
00059         _serial->getc();
00060     }
00061 }
00062 
00063 
00064 // read/write handling with timeouts
00065 int ATParser::write(const char *data, int size)
00066 {
00067     int i = 0;
00068     for ( ; i < size; i++) {
00069         if (putc(data[i]) < 0) {
00070             return -1;
00071         }
00072     }
00073     return i;
00074 }
00075 
00076 int ATParser::read(char *data, int size)
00077 {
00078     int i = 0;
00079     for ( ; i < size; i++) {
00080         int c = getc();
00081         if (c < 0) {
00082             return -1;
00083         }
00084         data[i] = c;
00085     }
00086     return i;
00087 }
00088 
00089 
00090 // printf/scanf handling
00091 int ATParser::vprintf(const char *format, va_list args)
00092 {
00093     if (vsprintf(_buffer, format, args) < 0) {
00094         return false;
00095     }
00096     int i = 0;
00097     for ( ; _buffer[i]; i++) {
00098         if (putc(_buffer[i]) < 0) {
00099             return -1;
00100         }
00101     }
00102     return i;
00103 }
00104 
00105 int ATParser::vscanf(const char *format, va_list args)
00106 {
00107     // Since format is const, we need to copy it into our buffer to
00108     // add the line's null terminator and clobber value-matches with asterisks.
00109     //
00110     // We just use the beginning of the buffer to avoid unnecessary allocations.
00111     int i = 0;
00112     int offset = 0;
00113 
00114     while (format[i]) {
00115         if (format[i] == '%' && format[i+1] != '%' && format[i+1] != '*') {
00116             _buffer[offset++] = '%';
00117             _buffer[offset++] = '*';
00118             i++;
00119         } else {
00120             _buffer[offset++] = format[i++];
00121         }
00122     }
00123 
00124     // Scanf has very poor support for catching errors
00125     // fortunately, we can abuse the %n specifier to determine
00126     // if the entire string was matched.
00127     _buffer[offset++] = '%';
00128     _buffer[offset++] = 'n';
00129     _buffer[offset++] = 0;
00130 
00131     // To workaround scanf's lack of error reporting, we actually
00132     // make two passes. One checks the validity with the modified
00133     // format string that only stores the matched characters (%n).
00134     // The other reads in the actual matched values.
00135     //
00136     // We keep trying the match until we succeed or some other error
00137     // derails us.
00138     int j = 0;
00139 
00140     while (true) {
00141         // Ran out of space
00142         if (j+1 >= _buffer_size - offset) {
00143             return false;
00144         }
00145         // Recieve next character
00146         int c = getc();
00147         if (c < 0) {
00148             return -1;
00149         }
00150         _buffer[offset + j++] = c;
00151         _buffer[offset + j] = 0;
00152 
00153         // Check for match
00154         int count = -1;
00155         sscanf(_buffer+offset, _buffer, &count);
00156 
00157         // We only succeed if all characters in the response are matched
00158         if (count == j) {
00159             // Store the found results
00160             vsscanf(_buffer+offset, format, args);
00161             return j;
00162         }
00163     }
00164 }
00165 
00166 
00167 // Command parsing with line handling
00168 bool ATParser::vsend(const char *command, va_list args)
00169 {
00170     // Create and send command
00171     if (vsprintf(_buffer, command, args) < 0) {
00172         return false;
00173     }
00174     for (int i = 0; _buffer[i]; i++) {
00175         if (putc(_buffer[i]) < 0) {
00176             return false;
00177         }
00178     }
00179 
00180     // Finish with newline
00181     for (int i = 0; _delimiter[i]; i++) {
00182         if (putc(_delimiter[i]) < 0) {
00183             return false;
00184         }
00185     }
00186 
00187     debug_if(dbg_on, "AT> %s\r\n", _buffer);
00188     return true;
00189 }
00190 
00191 bool ATParser::vrecv(const char *response, va_list args)
00192 {
00193     // Iterate through each line in the expected response
00194     while (response[0]) {
00195         // Since response is const, we need to copy it into our buffer to
00196         // add the line's null terminator and clobber value-matches with asterisks.
00197         //
00198         // We just use the beginning of the buffer to avoid unnecessary allocations.
00199         int i = 0;
00200         int offset = 0;
00201 
00202         while (response[i]) {
00203             if (memcmp(&response[i+1-_delim_size], _delimiter, _delim_size) == 0) {
00204                 i++;
00205                 break;
00206             } else if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') {
00207                 _buffer[offset++] = '%';
00208                 _buffer[offset++] = '*';
00209                 i++;
00210             } else {
00211                 _buffer[offset++] = response[i++];
00212             }
00213         }
00214 
00215         // Scanf has very poor support for catching errors
00216         // fortunately, we can abuse the %n specifier to determine
00217         // if the entire string was matched.
00218         _buffer[offset++] = '%';
00219         _buffer[offset++] = 'n';
00220         _buffer[offset++] = 0;
00221 
00222         // To workaround scanf's lack of error reporting, we actually
00223         // make two passes. One checks the validity with the modified
00224         // format string that only stores the matched characters (%n).
00225         // The other reads in the actual matched values.
00226         //
00227         // We keep trying the match until we succeed or some other error
00228         // derails us.
00229         int j = 0;
00230 
00231         while (true) {
00232             // Recieve next character
00233             int c = getc();
00234             if (c < 0) {
00235                 return false;
00236             }
00237             _buffer[offset + j++] = c;
00238             _buffer[offset + j] = 0;
00239 
00240             // Check for oob data
00241             for (int k = 0; k < _oobs.size(); k++) {
00242                 if (j == _oobs[k].len && memcmp(
00243                         _oobs[k].prefix, _buffer+offset, _oobs[k].len) == 0) {
00244                     debug_if(dbg_on, "AT! %s\r\n", _oobs[k].prefix);
00245                     _oobs[k].cb();
00246 
00247                     // oob may have corrupted non-reentrant buffer,
00248                     // so we need to set it up again
00249                     return vrecv(response, args);
00250                 }
00251             }
00252 
00253             // Check for match
00254             int count = -1;
00255             sscanf(_buffer+offset, _buffer, &count);
00256 
00257             // We only succeed if all characters in the response are matched
00258             if (count == j) {
00259                 debug_if(dbg_on, "AT= %s\r\n", _buffer+offset);
00260                 // Reuse the front end of the buffer
00261                 memcpy(_buffer, response, i);
00262                 _buffer[i] = 0;
00263 
00264                 // Store the found results
00265                 vsscanf(_buffer+offset, _buffer, args);
00266 
00267                 // Jump to next line and continue parsing
00268                 response += i;
00269                 break;
00270             }
00271 
00272             // Clear the buffer when we hit a newline or ran out of space
00273             // running out of space usually means we ran into binary data
00274             if (j+1 >= _buffer_size - offset ||
00275                 strcmp(&_buffer[offset + j-_delim_size], _delimiter) == 0) {
00276 
00277                 debug_if(dbg_on, "AT< %s", _buffer+offset);
00278                 j = 0;
00279             }
00280         }
00281     }
00282 
00283     return true;
00284 }
00285 
00286 
00287 // Mapping to vararg functions
00288 int ATParser::printf(const char *format, ...)
00289 {
00290     va_list args;
00291     va_start(args, format);
00292     int res = vprintf(format, args);
00293     va_end(args);
00294     return res;
00295 }
00296 
00297 int ATParser::scanf(const char *format, ...)
00298 {
00299     va_list args;
00300     va_start(args, format);
00301     int res = vscanf(format, args);
00302     va_end(args);
00303     return res;
00304 }
00305 
00306 bool ATParser::send(const char *command, ...)
00307 {
00308     va_list args;
00309     va_start(args, command);
00310     bool res = vsend(command, args);
00311     va_end(args);
00312     return res;
00313 }
00314 
00315 bool ATParser::recv(const char *response, ...)
00316 {
00317     va_list args;
00318     va_start(args, response);
00319     bool res = vrecv(response, args);
00320     va_end(args);
00321     return res;
00322 }
00323 
00324 
00325 // oob registration
00326 void ATParser::oob(const char *prefix, Callback<void()> cb)
00327 {
00328     struct oob oob;
00329     oob.len = strlen(prefix);
00330     oob.prefix = prefix;
00331     oob.cb = cb;
00332     _oobs.push_back(oob);
00333 }