Library for Modtronix NZ32 STM32 boards, like the NZ32-SC151, NZ32-SB072, NZ32-SE411 and others

Revision:
4:43abdd8eda40
Child:
7:709130701ac7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mx_cmd_buffer.h	Sun Aug 30 09:29:08 2015 +1000
@@ -0,0 +1,639 @@
+/**
+ * File:      mx_ascii_cmd_buffer.h
+ *
+ * Author:    Modtronix Engineering - www.modtronix.com
+ *
+ * Description:
+ *
+ * Software License Agreement:
+ * This software has been written or modified by Modtronix Engineering. The code
+ * may be modified and can be used free of charge for commercial and non commercial
+ * applications. If this is modified software, any license conditions from original
+ * software also apply. Any redistribution must include reference to 'Modtronix
+ * Engineering' and web link(www.modtronix.com) in the file header.
+ *
+ * THIS SOFTWARE IS PROVIDED IN AN 'AS IS' CONDITION. NO WARRANTIES, WHETHER EXPRESS,
+ * IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE
+ * COMPANY SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
+ * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
+ */
+#ifndef SRC_MX_CMD_BUFFER_H_
+#define SRC_MX_CMD_BUFFER_H_
+
+// This file contains an ASCII command buffer. It is used to store ASCII commands.
+// Each command ends with a ';' character. CR(0x0a='\r') and LF(0x0d='\n') are converted to ';' characters.
+// A ASCII command is an ASCII Formatted String, with Escape Sequences (Control Characters).
+// It uses 2 upper case characters to represent a single hex character.
+// Strings must be enclosed within single quotation marks(').
+// Lower case characters 'a' to 'z' are used to represent "Control characters". It has the following format:
+// HH   = Two upper case characters representing a single byte (hex value).
+// XHH  = Same as HH
+// NDD  = Decimal number. For example "N100" = decimal 100 t0='500'
+// c    = Lower case character 'a' to 'z' represents a "control character".
+// s, p = 's' an 'p' control characters represent the start and stop of a message.
+// ^^   = Two "escape characters" represents a single '^' character.
+// ^x   = Use this format to represent a "control character" that is not lower. Lower case characters do not have
+//        to be escaped. Currently not used, but reserved for future use!
+// '    = Enclose string with single quotes. A double ' character will not end the string, but represents a
+//        single ' character.
+//
+// Some Examples:
+// 30A8'abc' = 2 bytes (0x30, 0xA8), and 3 characters = 'abc'
+// 50800C'Hello'0A'World' = 3 bytes, 5 characters('Hello'), 1 byte, 5 characters('World')
+// 5588;
+
+#include "mx_buffer_base.h"
+#include "mx_circular_buffer.h"
+
+//Defines for MXH_DEBUG - debugging for include file
+#define DEBUG_ENABLE_MX_CMD_BUFFER          1
+#define DEBUG_ENABLE_INFO_MX_CMD_BUFFER     0
+
+#if !defined(MXH_DEBUG)
+#if (DEBUG_ENABLE_MX_CMD_BUFFER == 1)
+    #define MXH_DEBUG   MX_DEBUG
+    #if (DEBUG_ENABLE_INFO_MX_CMD_BUFFER == 1)
+        #define MXH_DEBUG_INFO   MX_DEBUG
+    #else
+        #define MXH_DEBUG_INFO(format, args...) ((void)0)
+    #endif
+#else
+    #define MXH_DEBUG(format, args...) ((void)0)
+    #define MXH_DEBUG_INFO(format, args...) ((void)0)
+#endif  // #if (DEBUG_ENABLE_MX_CMD_BUFFER == 1)
+#endif  // #if !defined(MXH_DEBUG)
+
+
+/** Templated Circular buffer class
+ */
+template<uint32_t BufferSize, uint8_t Commands = 16, typename CounterType = uint32_t>
+class MxCmdBuffer : public MxBuffer {
+public:
+    MxCmdBuffer() : _head(0), _tail(0), _headStartOfNxtCmd(0), _full(false),
+            _errBufFull(0), _dontSaveCurrentCommand(0), flags(0)
+    {
+        //flags.Val = 0;
+    }
+
+    ~MxCmdBuffer() {
+    }
+
+    /** Adds a byte to the current command in the buffer. This function checks for the "End of Command" character,
+     * and if found, increments the "command count" register indicating how many commands this buffer has.
+     *
+     * If the buffer becomes full before the "End of Command" character is reached, all bytes added for current
+     * command are removed. And, all following bytes added until the next "End of Command" character will be ignored.
+     *
+     * The checkBufferFullError() function can be used to check if this function caused an error, and command was
+     * not added to buffer.
+     *
+     * @param data Data to be pushed to the buffer
+     *
+     * @return true if character added to buffer, else false. Important to note that all characters added to
+     *      current command CAN BE REMOVED if buffer gets full before "End of Command" added to buffer!
+     */
+    bool put(const uint8_t& data) {
+        uint8_t c = data;
+
+        //Check if buffer full! If so:
+        // - All data for this command added till now is lost
+        // - All future data added for this command is ignored, until next "End of command" character is received
+        if (isFull() && (_dontSaveCurrentCommand==false)) {
+            _dontSaveCurrentCommand = true;
+            _errBufFull = true;
+            MXH_DEBUG("\r\nBuffer full, cmd LOST!");
+
+            //Remove all character added for this command. Restore head to last "End of Command" pointer
+            if (cmdEndsBuf.isEmpty() == false) {
+                //Restore head to byte following loast "End of Command" pointer
+                _head = ((cmdEndsBuf.peekLastAdded()+1)%BufferSize);
+            }
+            //If no commands in buffer, set head=tail
+            else {
+                _head = _tail;
+                _full = false;
+            }
+        }
+
+        //Check if "End of Command" byte. A command string is terminated with a ';', CR(0x0a='\r') or LF(0x0d='\n') character
+        if ((c == ';') || (c == 0x0d) || (c == 0x0a)) {
+            if (flags.bits.replaceCrLfWithEoc) {
+                c = ';';    //Change to "end of command" character
+            }
+
+            if (_dontSaveCurrentCommand == true) {
+                _dontSaveCurrentCommand = false;
+                _headStartOfNxtCmd = _head;
+                return false;   //Nothing added
+            }
+            else {
+                //If multiple "end of command" characters received, ignore them.
+                if (_headStartOfNxtCmd == _head) {
+                    //Nothing to do, exit
+                    MXH_DEBUG_INFO("\r\nMultiple EOC");
+                    return false;   //Nothing added
+                }
+
+                cmdEndsBuf.put(_head);  //Add pointer to "end of command" character
+                //End of command character will be added to buffer below at current _head pointer
+                MXH_DEBUG_INFO("\r\nAdded Cmd, EOC=%d", _head);
+
+                //Save start of next command = byte following current _head
+                _headStartOfNxtCmd = ((_head+1)%BufferSize);
+                MXH_DEBUG_INFO(" Nxt=%d", _headStartOfNxtCmd);
+            }
+        }
+
+        if (_dontSaveCurrentCommand) {
+            return false;
+        }
+
+        //Add byte to buffer
+        _pool[_head++] = c;
+        _head %= BufferSize;
+        if (_head == _tail) {
+            _full = true;
+        }
+
+        return true;
+    }
+
+    /** Adds given NULL terminated string to the buffer. This function checks for the "End of Command" character,
+     * and if found, increments the "command count" register indicating how many commands this buffer has.
+     *
+     * If the buffer becomes full before the "End of Command" character is reached, all bytes added for current
+     * command are removed. And, all following bytes added until the next "End of Command" character will be ignored.
+     *
+     * The checkBufferFullError() function can be used to check if this function caused an error, and command was
+     * not added to buffer.
+     *
+     * @param buf Source buffer containing array to add to buffer
+     * @param bufSize Size of array to add to buffer
+     *
+     * @return Returns true if something added to buffer, else false. Important to note that all characters added to
+     *      current command CAN BE REMOVED if buffer gets full before "End of Command" added to buffer!
+     */
+    bool put(const char* str) {
+        bool retVal = 0;
+
+        //DO NOT DO this check here! This check MUST be done by put() function, because if buffer becomes full before
+        //"End of Command" character received, the put() function will remove all current command characters added!
+        //if (isFull() == true) {
+        //    return false;
+        //}
+
+        //Add whole string to buffer. DO NOT check isFull() in next line, MUST be checked in put() below!!!!
+        while((*str != 0) /*&& (isFull()==false)*/) {
+            retVal = retVal | put((uint8_t)(*str++));
+        }
+        return retVal;
+    }
+
+
+    /** Adds given array to the buffer. This function checks for the "End of Command" character,
+     * and if found, increments the "command count" register indicating how many commands this buffer has.
+     *
+     * If the buffer becomes full before the "End of Command" character is reached, all bytes added for current
+     * command are removed. And, all following bytes added until the next "End of Command" character will be ignored.
+     *
+     * The checkBufferFullError() function can be used to check if this function caused an error, and command was
+     * not added to buffer.
+
+     * @param buf Source buffer containing array to add to buffer
+     * @param bufSize Size of array to add to buffer
+     *
+     * @return Returns true if something added to buffer, else false. Important to note that all characters added to
+     *      current command CAN BE REMOVED if buffer gets full before "End of Command" added to buffer!
+     */
+    bool putArray(uint8_t* buf, uint16_t bufSize) {
+        bool retVal = 0;
+        int i;
+
+        //DO NOT DO this check here! This check MUST be done by put() function, because if buffer becomes full before
+        //"End of Command" character received, the put() function will remove all current command characters added!
+        //if (getFree() < bufSize) {
+        //    return 0;
+        //}
+
+        for(i=0; i<bufSize; i++) {
+            retVal = retVal | put(buf[i]);
+        }
+        return retVal;
+    }
+
+    /** Gets and object from the buffer. Ensure buffer is NOT empty before calling this function!
+     *
+     * @return Read data
+     */
+    uint8_t get() {
+        if (!isEmpty()) {
+            uint8_t retData;
+            retData = _pool[_tail++];
+            _tail %= BufferSize;
+            _full = false;
+            return retData;
+        }
+        return 0;
+    }
+
+    /** Gets and object from the buffer. Returns true if OK, else false
+     *
+     * @param data Variable to put read data into
+     * @return True if the buffer is not empty and data contains a transaction, false otherwise
+     */
+    bool getAndCheck(uint8_t& data) {
+        if (!isEmpty()) {
+            data = _pool[_tail++];
+            _tail %= BufferSize;
+            _full = false;
+            return true;
+        }
+        return false;
+    }
+
+
+    /** Gets and object from the buffer, but do NOT remove it. Ensure buffer is NOT empty before calling
+     * this function! If buffer is empty, will return an undefined value.
+     *
+     * @return Read data
+     */
+    uint8_t peek() {
+        return _pool[_tail];
+    }
+
+    /** Gets an object from the buffer at given offset, but do NOT remove it. Given offset is a value from
+     * 0 to n. Ensure buffer has as many objects as the offset requested! For example, if buffer has 5 objects
+     * available, given offset can be a value from 0 to 4.
+     *
+     * @param offset Offset of requested object. A value from 0-n, where (n+1) = available objects = getAvailable()
+     * @return Object at given offset
+     */
+    uint8_t peekAt(CounterType offset) {
+        return _pool[(offset+_tail)%BufferSize];
+    }
+
+    /** Gets the last object added to the buffer, but do NOT remove it.
+     *
+     * @return Object at given offset
+     */
+    uint8_t peekLastAdded() {
+        return _pool[(_head-1)%BufferSize];
+    }
+
+    /** Gets and array of given size, and write it to given buffer.
+     * Nothing is removed from buffer
+     *
+     * @param buf Destination buffer to array to
+     * @param bufSize Maximum size to write to destination buffer
+     * @param lenReq Requested length
+     *
+     *
+     * @return Number of bytes written to given buffer
+     */
+    uint16_t peekArray(uint8_t* buf, uint16_t bufSize, CounterType lenReq) {
+        uint16_t lenWritten=0;
+        CounterType currTail;
+        currTail = _tail;
+
+        //Some checks
+        if (isEmpty() || (bufSize==0) || (lenReq==0)) {
+            return 0;
+        }
+
+        do  {
+            buf[lenWritten++] = _pool[currTail];
+            currTail = ((currTail+1)%BufferSize);
+        } while((lenWritten<bufSize) && (lenWritten<lenReq));
+
+
+        return lenWritten;
+    }
+
+    /** Check if the buffer has a complete command
+     *
+     * @return True if the buffer has a command, false if not
+     */
+    bool hasCommand() {
+        return !cmdEndsBuf.isEmpty();
+    }
+
+    /** Get length of next command in buffer, excluding "end of command" character! For example,
+     * the command "r=100;" will return 5.
+     * @return Length of next command in buffer
+     */
+    uint8_t getCommandLength() {
+        CounterType offsetEOC;
+
+        if (cmdEndsBuf.isEmpty()) {
+            return 0;
+        }
+
+        //Get offset of "end of command" character of current command in buffer
+        offsetEOC = cmdEndsBuf.peek();
+
+        return ((offsetEOC-_tail) % BufferSize);
+    }
+
+    /** Get number of commands waiting in buffer
+     * @return Number of commands waiting in buffer
+     */
+    uint8_t getCommandsAvailable() {
+        return cmdEndsBuf.getAvailable();
+    }
+
+    /** If current command has "name=value" format, the 'name' part is returned.
+     * Else, the whole command is returned(excluding a possible trailing '=' character).
+     * Returned string is NULL terminated by default.
+     * Nothing is removed from the buffer!
+     *
+     * If true is returned in the "isNameValue" parameter, this is a "name=value" command, with a name part of at
+     * lease 1 character long. The offset of the 'value' part will be the returned
+     *
+     * If false is returned in the "isNameValue" parameter, there is no 'value' part of at least 1 character. There could however
+     * still be a '=' character with no 'value' following it. Any possible trailing '=' character is removed string returned in 'buf'.
+     *
+     * @param buf Destination buffer to write string to
+     *
+     * @param bufSize Maximum size to write to destination buffer
+     *
+     * @param isNameValue Returns true if a '=' character was found -AND- at least 1 char following it. This indicates
+     *        this is a "name=value" command, and has a value part(of at least 1 character) following the '='.
+     *        First character of 'value' is value returned by this function + 1!
+     *
+     * @return Size in bytes of string returned in 'buf' parameter(is 'name' if isNameValue=true).
+     *         If true is returned in 'isNameValue', 'buf' contains the 'name' part of a 'name=value' command. This returned offset points to '='.
+     *         If false is returned in 'isNameValue', 'buf' contains the whole command string(excluding possible trailing '=' character).
+     */
+    uint16_t getCommandName(uint8_t* buf, uint16_t bufSize, bool& isNameValue, bool nullTerminate = true) {
+        CounterType offsetEOC;
+        CounterType currTail;
+        uint16_t nameLen = 0;
+        isNameValue=false;
+
+        //Some checks
+        if (cmdEndsBuf.isEmpty() || (bufSize==0) || (_pool[_tail]=='=')) {
+            return 0;
+        }
+        offsetEOC = cmdEndsBuf.peek();
+        currTail = _tail;
+
+        //Copy current command string to given buffer, until '=' reached
+        do {
+            buf[nameLen++] = _pool[currTail];
+            currTail = ((currTail+1)%BufferSize);
+            //If next character is '='
+            if(_pool[currTail] == '=') {
+                //Check at least 1 character following '='
+                //Get pointer of character following '=', and ensure it is not "end of command"(offsetEOC). Meaning there is
+                //still at least 1 more character following '='
+                if(((currTail+1)%BufferSize) != offsetEOC) {
+                    isNameValue = true;
+                }
+                break;
+            }
+        } while((currTail!=offsetEOC) && (nameLen<bufSize));
+
+
+        if (nullTerminate) {
+            if(nameLen<bufSize) {
+                buf[nameLen] = 0;
+            }
+            buf[bufSize-1] = 0; //Null terminate last position of buffer in case it filled up
+        }
+
+        return nameLen;
+    }
+
+    /** For commands with "name=value" format, returns the 'value' part.
+     * Returned string is NULL terminated by default.
+     * Nothing is removed from the buffer!
+     *
+     * To optimize this function, call getCommandName() before calling this function. The getCommandName() return value(+1)
+     * can be used as 'offset' parameter to this function. This will save this function from searching for '=' character.
+     *
+     *
+     * @param buf Destination buffer to write string to
+     * @param bufSize Maximum size to write to destination buffer
+     * @param offset Gives offset of value string if known. This can be value(+1) returned by
+     *        getCommandName() function. Use -1 if unknown.
+     *
+     * @return Size in bytes of returned string
+     */
+    uint16_t getCommandValue(uint8_t* buf, uint16_t bufSize, uint16_t offset = -1, bool nullTerminate = true) {
+        CounterType offsetEOC;
+        CounterType currTail;
+        uint16_t valueLen = 0;
+        bool foundEq=false;     //'=' character found
+
+        //Some checks
+        if (cmdEndsBuf.isEmpty() || (bufSize==0)) {
+            return 0;
+        }
+        offsetEOC = cmdEndsBuf.peek();  //offset of "end of command" character
+
+        currTail = _tail;
+
+        //If offset was given, it will point to first character of value string
+        if (offset != -1) {
+            //Check offset point somewhere inside current command! Where ((offsetEOC-_tail) % BufferSize) = command length
+            if (offset < ((offsetEOC-_tail)%BufferSize)) {
+                currTail = ((currTail + offset)%BufferSize);    //Add offset to tail
+
+                //If given offset was for first character of 'value', it will NOT point to '='. It will be next character.
+                if(_pool[currTail] != '=') {
+                    //Points to character after '=', indicate '=' has already been found.
+                    foundEq=true;
+                }
+                //ELSE, assume offset for for '=' character. It will be found in step below
+            }
+            else {
+                //currTail = _tail; //Ignore given offset, and search whole command for '='
+                MXH_DEBUG("\r\ngetCommandValue() Offset error!");
+            }
+        }
+
+        do {
+            if (foundEq) {
+                buf[valueLen++] = _pool[currTail];
+            }
+            else {
+                if(_pool[currTail] == '=') {
+                    foundEq=true;
+                }
+            }
+            currTail = ((currTail+1)%BufferSize);
+        } while((currTail != offsetEOC) && (valueLen<bufSize));
+
+        if (nullTerminate) {
+            if(valueLen<bufSize) {
+                buf[valueLen] = 0;
+            }
+            buf[bufSize-1] = 0; //Null terminate last position of buffer in case it filled up
+        }
+
+        return valueLen;
+    }
+
+    /** Search current command for given character
+     *
+     * @param offset Returns offset of found character. If NULL, this parameter is not used.
+     *
+     * @return Returns true if found, else false
+     */
+    bool search(uint8_t c, uint16_t* offsetFound) {
+        CounterType offsetEOF;
+        CounterType currTail;
+
+        //Get command length
+        if (cmdEndsBuf.isEmpty()) {
+            return false;
+        }
+        offsetEOF = cmdEndsBuf.peek();
+        currTail = _tail;
+        do {
+            if(_pool[currTail] == c) {
+                if (offsetFound!=NULL) {
+                    *offsetFound = (uint16_t)currTail;
+                }
+                return true;
+            }
+            currTail = ((currTail+1)%BufferSize);
+        } while(currTail != offsetEOF);
+
+//        CounterType i;
+//        CounterType cmdLen;
+//        cmdLen = (offsetEOF-_tail) % BufferSize;
+//        for (i=0; i<cmdLen; i++) {
+//            if(_pool[(i+_tail)%BufferSize] == c) {
+//            //if(peekAt(i)==c) {
+//                offsetFound = (uint16_t)i;
+//                return true;
+//            }
+//        }
+        return false;
+    }
+
+    /** Check if the buffer is empty
+     *
+     * @return True if the buffer is empty, false if not
+     */
+    bool isEmpty() {
+        return (_head == _tail) && !_full;
+    }
+
+    /** Check if the buffer is full
+     *
+     * @return True if the buffer is full, false if not
+     */
+    bool isFull() {
+        return _full;
+    }
+
+    /** Get number of available bytes in buffer
+     * @return Number of available bytes in buffer
+     */
+    CounterType getAvailable() {
+        CounterType avail;
+        if (isEmpty()) {
+            return 0;
+        }
+        avail = _head - _tail;
+        avail %= BufferSize;
+        return avail;
+    }
+
+
+    /** Get number of free bytes in buffer available for writing data to.
+     * @return Number of free bytes in buffer available for writing data to.
+     */
+    CounterType getFree() {
+        CounterType free;
+        //Full
+        if (_full==true) {
+            return 0;
+        }
+        //Empty
+        if(_head == _tail) {
+            return BufferSize;
+        }
+        free = _tail - _head;
+        free %= BufferSize;
+        return free;
+    }
+
+    /** Replaces any LF or CR characters with an "End of Command" character = ';'
+     */
+    void enableReplaceCrLfWithEoc() {
+        flags.bits.replaceCrLfWithEoc = true;
+    }
+
+    /** Do NOT replaces LF and CR characters with an "End of Command" character = ';'
+     */
+    void disableReplaceCrLfWithEoc() {
+        flags.bits.replaceCrLfWithEoc = false;
+    }
+
+    /** Check if an overwrite error occurred. It resets the error flag if it was set
+     * @return True if overwrite error occurred since last time this function was called, else false
+     */
+    bool checkBufferFullError() {
+        bool retVal = _errBufFull;
+        _errBufFull = false;
+        return retVal;
+    }
+
+    /** Reset the buffer
+     */
+    void reset() {
+        _head = 0;
+        _headStartOfNxtCmd = 0;
+        _tail = 0;
+        _full = false;
+        _errBufFull = false;
+        _dontSaveCurrentCommand = false;
+    }
+
+    /** Remove a command from buffer
+     */
+    void removeCommand() {
+        if (cmdEndsBuf.isEmpty()) {
+            return;
+        }
+
+        //MXH_DEBUG("\r\nRemoving Cmd");
+
+        //Set tail = "end of command" character + 1. This is first character of next command
+        _tail = (cmdEndsBuf.get()+1) % BufferSize;
+        _full = false;
+    }
+
+private:
+    uint8_t _pool[BufferSize];
+    volatile CounterType _head;
+    volatile CounterType _tail;
+    //_head position of first character of next command = position of first byte after last "end of command" character was added
+    volatile CounterType _headStartOfNxtCmd;
+    volatile bool _full;
+    volatile bool _errBufFull;
+    volatile bool _dontSaveCurrentCommand;
+
+    union Flags {
+        struct {
+            uint8_t replaceCrLfWithEoc : 1;         //Replace CR and LF with "End of Command" character = ';'
+        } bits;
+        uint8_t     Val;
+        //Union constructor. Used in initialization list of this class.
+        Flags(uint8_t v) : Val(v) {}
+    } flags;
+
+public:
+    //Buffer for storing "end of command" locations
+    MxCircularBuffer <uint16_t, Commands, CounterType> cmdEndsBuf;
+    //MxCircularBuffer <uint16_t, Commands, uint16_t> cmdEndsBuf;   //Creates larger code
+};
+
+
+
+
+#endif /* SRC_MX_CMD_BUFFER_H_ */