mirror of
				https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
				synced 2025-10-31 03:14:16 +00:00 
			
		
		
		
	
		
			
	
	
		
			363 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			363 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | /*
 | ||
|  |  Stream.cpp - adds parsing methods to Stream class | ||
|  |  Copyright (c) 2008 David A. Mellis.  All right reserved. | ||
|  | 
 | ||
|  |  This library is free software; you can redistribute it and/or | ||
|  |  modify it under the terms of the GNU Lesser General Public | ||
|  |  License as published by the Free Software Foundation; either | ||
|  |  version 2.1 of the License, or (at your option) any later version. | ||
|  | 
 | ||
|  |  This library is distributed in the hope that it will be useful, | ||
|  |  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||
|  |  Lesser General Public License for more details. | ||
|  | 
 | ||
|  |  You should have received a copy of the GNU Lesser General Public | ||
|  |  License along with this library; if not, write to the Free Software | ||
|  |  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||
|  | 
 | ||
|  |  Created July 2011 | ||
|  |  parsing functions based on TextFinder library by Michael Margolis | ||
|  |  */ | ||
|  | 
 | ||
|  | #include "Arduino.h"
 | ||
|  | #include "Stream.h"
 | ||
|  | 
 | ||
|  | // if/branch optimization
 | ||
|  | #define UNLIKELY(x) (__builtin_expect (!!(x), 0))
 | ||
|  | #define LIKELY(x) (__builtin_expect (!!(x), 1))
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #define PARSE_TIMEOUT 1000  // default number of milli-seconds to wait
 | ||
|  | #define NO_SKIP_CHAR  1  // a magic char not found in a valid ASCII numeric field
 | ||
|  | 
 | ||
|  | // private method to read stream with timeout
 | ||
|  | int Stream::timedRead() | ||
|  | { | ||
|  |   int c; | ||
|  | 
 | ||
|  |   _startMillis = millis(); | ||
|  | 
 | ||
|  |   do | ||
|  |   { | ||
|  |     c = read(); | ||
|  | 
 | ||
|  |     if (c >= 0) | ||
|  |       return c; | ||
|  | 
 | ||
|  |     wait_for_interrupt(); // XMega enhancement
 | ||
|  | 
 | ||
|  |   } while(millis() - _startMillis < _timeout); | ||
|  | 
 | ||
|  |   return -1;     // -1 indicates timeout
 | ||
|  | } | ||
|  | 
 | ||
|  | // private method to peek stream with timeout
 | ||
|  | int Stream::timedPeek() | ||
|  | { | ||
|  |   int c; | ||
|  | 
 | ||
|  |   _startMillis = millis(); | ||
|  | 
 | ||
|  |   do | ||
|  |   { | ||
|  |     c = peek(); | ||
|  | 
 | ||
|  |     if (c >= 0) | ||
|  |       return c; | ||
|  | 
 | ||
|  |     wait_for_interrupt(); // XMega enhancement
 | ||
|  | 
 | ||
|  |   } while(millis() - _startMillis < _timeout); | ||
|  | 
 | ||
|  |   return -1;     // -1 indicates timeout
 | ||
|  | } | ||
|  | 
 | ||
|  | // returns peek of the next digit in the stream or -1 if timeout
 | ||
|  | // discards non-numeric characters
 | ||
|  | int Stream::peekNextDigit() | ||
|  | { | ||
|  |   int c; | ||
|  |   while (1) | ||
|  |   { | ||
|  |     c = timedPeek(); | ||
|  | 
 | ||
|  |   // refactored this for (possibly) better efficiency AND readability - seems to be the same size/footprint
 | ||
|  | //    if (c < 0)
 | ||
|  | //      return c;  // timeout
 | ||
|  | //
 | ||
|  | //    if (c == '-')
 | ||
|  | //      return c;
 | ||
|  | //
 | ||
|  | //    if (c >= '0' && c <= '9')
 | ||
|  | //      return c;
 | ||
|  | 
 | ||
|  |     if(UNLIKELY(c < 0) ||             // refactored.  < 0 is a timeout
 | ||
|  |        UNLIKELY(c == '-') ||          // negative (TODO: test for leading char only, or lead on exponent)
 | ||
|  |        LIKELY(c >= '0' && c <= '9'))  // numeric digit; TODO test for exponent on float?
 | ||
|  |     { | ||
|  |       return c; | ||
|  |     } | ||
|  | 
 | ||
|  |     read();  // discard non-numeric
 | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | // Public Methods
 | ||
|  | //////////////////////////////////////////////////////////////
 | ||
|  | 
 | ||
|  | void Stream::setTimeout(unsigned long timeout)  // sets the maximum number of milliseconds to wait
 | ||
|  | { | ||
|  |   _timeout = timeout; | ||
|  | } | ||
|  | 
 | ||
|  |  // find returns true if the target string is found
 | ||
|  | bool  Stream::find(char *target) | ||
|  | { | ||
|  |   return findUntil(target, NULL/*""*/); | ||
|  | } | ||
|  | 
 | ||
|  | // reads data from the stream until the target string of given length is found
 | ||
|  | // returns true if target string is found, false if timed out
 | ||
|  | bool Stream::find(char *target, size_t length) | ||
|  | { | ||
|  |   return findUntil(target, length, NULL, 0); | ||
|  | } | ||
|  | 
 | ||
|  | // as find but search ends if the terminator string is found
 | ||
|  | bool  Stream::findUntil(char *target, char *terminator) | ||
|  | { | ||
|  |   return findUntil(target, strlen(target), terminator, strlen(terminator)); | ||
|  | } | ||
|  | 
 | ||
|  | // reads data from the stream until the target string of the given length is found
 | ||
|  | // search terminated if the terminator string is found
 | ||
|  | // returns true if target string is found, false if terminated or timed out
 | ||
|  | bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t termLen) | ||
|  | { | ||
|  |   size_t index = 0;  // maximum target string length is 64k bytes!
 | ||
|  |   size_t termIndex = 0; | ||
|  |   int c; | ||
|  |    | ||
|  |   if(!target || *target == 0) // update, allow NULL pointer
 | ||
|  |   { | ||
|  |     return true;   // return true if target is a null string
 | ||
|  |   } | ||
|  | 
 | ||
|  |   while( (c = timedRead()) > 0) | ||
|  |   { | ||
|  |      | ||
|  |     if(c != target[index]) | ||
|  |     { | ||
|  |       index = 0; // reset index if any char does not match
 | ||
|  |     } | ||
|  | 
 | ||
|  |     if( c == target[index]) | ||
|  |     { | ||
|  |       //////Serial.print("found "); Serial.write(c); Serial.print("index now"); Serial.println(index+1);
 | ||
|  |       if(++index >= targetLen)  // return true if all chars in the target match
 | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     // allow for NULL 'terminator'    
 | ||
|  |     if(terminator && termLen > 0 && | ||
|  |        UNLIKELY(c == terminator[termIndex])) | ||
|  |     { | ||
|  |       if(++termIndex >= termLen) | ||
|  |       { | ||
|  |         return false;       // return false if terminate string found before target string
 | ||
|  |       } | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |       termIndex = 0; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   return false; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | // returns the first valid (long) integer value from the current position.
 | ||
|  | // initial characters that are not digits (or the minus sign) are skipped
 | ||
|  | // function is terminated by the first character that is not a digit.
 | ||
|  | long Stream::parseInt() | ||
|  | { | ||
|  |   return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
 | ||
|  | } | ||
|  | 
 | ||
|  | // as above but a given skipChar is ignored
 | ||
|  | // this allows format characters (typically commas) in values to be ignored
 | ||
|  | long Stream::parseInt(char skipChar) | ||
|  | { | ||
|  |   boolean isNegative = false; | ||
|  |   long value = 0; | ||
|  |   int c; | ||
|  | 
 | ||
|  |   c = peekNextDigit(); | ||
|  |   // ignore non numeric leading characters
 | ||
|  |   if(c < 0) | ||
|  |     return 0; // zero returned if timeout
 | ||
|  | 
 | ||
|  |   do | ||
|  |   { | ||
|  |     if(c == skipChar) | ||
|  |       ; // ignore this charactor
 | ||
|  |     else if(c == '-') | ||
|  |       isNegative = true; | ||
|  |     else if(c >= '0' && c <= '9')        // is c a digit?
 | ||
|  |       value = value * 10 + c - '0'; | ||
|  |     read();  // consume the character we got with peek
 | ||
|  |     c = timedPeek(); | ||
|  |   } | ||
|  |   while( (c >= '0' && c <= '9') || c == skipChar ) | ||
|  |     ; | ||
|  | 
 | ||
|  |   if(isNegative) | ||
|  |     value = -value; | ||
|  | 
 | ||
|  |   return value; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | // as parseInt but returns a floating point value
 | ||
|  | float Stream::parseFloat() | ||
|  | { | ||
|  |   return parseFloat(NO_SKIP_CHAR); | ||
|  | } | ||
|  | 
 | ||
|  | // as above but the given skipChar is ignored
 | ||
|  | // this allows format characters (typically commas) in values to be ignored
 | ||
|  | float Stream::parseFloat(char skipChar) | ||
|  | { | ||
|  |   boolean isNegative = false; | ||
|  |   boolean isFraction = false; | ||
|  |   long value = 0; | ||
|  |   char c; | ||
|  |   float fraction = 1.0; | ||
|  | 
 | ||
|  |   c = peekNextDigit(); | ||
|  |     // ignore non numeric leading characters
 | ||
|  |   if(c < 0) | ||
|  |   { | ||
|  |     return 0; // zero returned if timeout
 | ||
|  |   } | ||
|  | 
 | ||
|  |   do | ||
|  |   { | ||
|  |     if(c == skipChar) | ||
|  |     { | ||
|  |       // ignore
 | ||
|  |     } | ||
|  |     else if(c == '-') | ||
|  |     { | ||
|  |       isNegative = true; | ||
|  |     } | ||
|  |     else if (c == '.') | ||
|  |     { | ||
|  |       isFraction = true; | ||
|  |     } | ||
|  |     else if(c >= '0' && c <= '9')      // is c a digit?
 | ||
|  |     { | ||
|  |       value = value * 10 + c - '0'; | ||
|  | 
 | ||
|  |       if(isFraction) | ||
|  |       { | ||
|  |         fraction *= 0.1; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     read();  // consume the character we got with peek
 | ||
|  |     c = timedPeek(); | ||
|  |   } | ||
|  | 
 | ||
|  |   while( (c >= '0' && c <= '9')  || c == '.' || c == skipChar ) | ||
|  |     ; | ||
|  | 
 | ||
|  |   if(isNegative) | ||
|  |     value = -value; | ||
|  |   if(isFraction) | ||
|  |     return value * fraction; | ||
|  |   else | ||
|  |     return value; | ||
|  | } | ||
|  | 
 | ||
|  | // read characters from stream into buffer
 | ||
|  | // terminates if length characters have been read, or timeout (see setTimeout)
 | ||
|  | // returns the number of characters placed in the buffer
 | ||
|  | // the buffer is NOT null terminated.
 | ||
|  | //
 | ||
|  | size_t Stream::readBytes(char *buffer, size_t length) | ||
|  | { | ||
|  |   size_t count = 0; | ||
|  | 
 | ||
|  |   while (count < length) | ||
|  |   { | ||
|  |     int c = timedRead(); | ||
|  | 
 | ||
|  |     if (c < 0) | ||
|  |       break; | ||
|  | 
 | ||
|  |     *buffer++ = (char)c; | ||
|  |     count++; | ||
|  |   } | ||
|  | 
 | ||
|  |   return count; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | // as readBytes with terminator character
 | ||
|  | // terminates if length characters have been read, timeout, or if the terminator character  detected
 | ||
|  | // returns the number of characters placed in the buffer (0 means no valid data found)
 | ||
|  | 
 | ||
|  | size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) | ||
|  | { | ||
|  |   if (length < 1) | ||
|  |     return 0; | ||
|  | 
 | ||
|  |   size_t index = 0; | ||
|  | 
 | ||
|  |   while (index < length) | ||
|  |   { | ||
|  |     int c = timedRead(); | ||
|  |     if (c < 0 || c == terminator) break; | ||
|  |     *buffer++ = (char)c; | ||
|  |     index++; | ||
|  |   } | ||
|  | 
 | ||
|  |   return index; // return number of characters, not including null terminator
 | ||
|  | } | ||
|  | 
 | ||
|  | String Stream::readString() | ||
|  | { | ||
|  |   String ret; | ||
|  | 
 | ||
|  |   int c = timedRead(); | ||
|  | 
 | ||
|  |   while (c >= 0) | ||
|  |   { | ||
|  |     ret += (char)c; | ||
|  |     c = timedRead(); | ||
|  |   } | ||
|  | 
 | ||
|  |   return ret; | ||
|  | } | ||
|  | 
 | ||
|  | String Stream::readStringUntil(char terminator) | ||
|  | { | ||
|  |   String ret; | ||
|  | 
 | ||
|  |   int c = timedRead(); | ||
|  | 
 | ||
|  |   while (c >= 0 && c != terminator) | ||
|  |   { | ||
|  |     ret += (char)c; | ||
|  |     c = timedRead(); | ||
|  |   } | ||
|  | 
 | ||
|  |   return ret; | ||
|  | } | ||
|  | 
 |