From 383d7fb207bd70ad6e287ff424f41f048baff8cf Mon Sep 17 00:00:00 2001 From: Timo Lappalainen Date: Tue, 6 Feb 2024 11:08:41 +0200 Subject: [PATCH] Fix for tPriorityRingBuffer and SetN2kPGN129284/ParseN2kPGN129284 - Fix tPriorityRingBuffer. It did not freed values properly. tPriorityRingBuffer is used on tNMEA2000_Teensyx driver. - Ring buffer documentation update. - Fixed SetN2kPGN129284 and ParseN2kPGN129284. Changed OriginWaypointNumber and DestinationWaypointNumber parameter to uint32_t. This causes compatibility issue for users used ParseN2kPGN129284 and they have to update reference parameter type. Also if SetN2kPGN129284 was used with related parameters value N2kUint8NA, they must be updated to use N2kUint32NA - Moved OnOpen call on Open after setting Heartbeat interval and offset. If Heartbeat settings were set on OnOpen, settings were reset. - Added note to documentation for OnOpen about coming change. - Added lightning PGNs to fastpacket list. --- Documents/src/changes.md | 9 ++ library.json | 2 +- library.properties | 2 +- src/N2kMessages.cpp | 4 +- src/N2kMessages.h | 8 +- src/NMEA2000.cpp | 45 ++++--- src/NMEA2000.h | 35 ++++-- src/RingBuffer.h | 256 +++++++++++++++++++++++++++------------ src/RingBuffer.tpp | 46 ++++++- 9 files changed, 292 insertions(+), 115 deletions(-) diff --git a/Documents/src/changes.md b/Documents/src/changes.md index 8a49042e..51dc5ce4 100644 --- a/Documents/src/changes.md +++ b/Documents/src/changes.md @@ -1,6 +1,15 @@ # Changes to the Library {#changes} \tableofcontents +## 06.02.2024 + +- Fix tPriorityRingBuffer. It did not freed values properly. tPriorityRingBuffer is used on tNMEA2000_Teensyx driver. +- Ring buffer documentation update. +- Fixed SetN2kPGN129284 and ParseN2kPGN129284. Changed OriginWaypointNumber and DestinationWaypointNumber parameter to uint32_t. This causes compatibility issue for users used ParseN2kPGN129284 and they have to update reference parameter type. Also if SetN2kPGN129284 was used with related parameters value N2kUint8NA, they must be updated to use N2kUint32NA +- Moved OnOpen call on Open after setting Heartbeat interval and offset. If Heartbeat settings were set on OnOpen, settings were reset. +- Added note to documentation for OnOpen about coming change. +- Added lightning PGNs to fastpacket list. + ## 02.02.2024 - Document update. Updates on document sources and code sources. diff --git a/library.json b/library.json index 800a53ff..2f61627a 100644 --- a/library.json +++ b/library.json @@ -13,7 +13,7 @@ "url": "http://www.kave.fi", "maintainer": true }, - "version": "4.20.2", + "version": "4.20.3", "license": "MIT", "frameworks": "*", "platforms": "*" diff --git a/library.properties b/library.properties index e4f81f79..0ef1b7cd 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=NMEA2000 -version=4.20.2 +version=4.20.3 author=Timo Lappalainen maintainer=Kave Oy sentence=NMEA 2000 library for building compatible devices for NMEA 2000 bus. diff --git a/src/N2kMessages.cpp b/src/N2kMessages.cpp index dc525f40..ab89f33c 100644 --- a/src/N2kMessages.cpp +++ b/src/N2kMessages.cpp @@ -1522,7 +1522,7 @@ bool ParseN2kPGN129283(const tN2kMsg &N2kMsg, unsigned char& SID, tN2kXTEMode& X void SetN2kPGN129284(tN2kMsg &N2kMsg, unsigned char SID, double DistanceToWaypoint, tN2kHeadingReference BearingReference, bool PerpendicularCrossed, bool ArrivalCircleEntered, tN2kDistanceCalculationType CalculationType, double ETATime, int16_t ETADate, double BearingOriginToDestinationWaypoint, double BearingPositionToDestinationWaypoint, - uint8_t OriginWaypointNumber, uint8_t DestinationWaypointNumber, + uint32_t OriginWaypointNumber, uint32_t DestinationWaypointNumber, double DestinationLatitude, double DestinationLongitude, double WaypointClosingVelocity) { N2kMsg.SetPGN(129284L); N2kMsg.Priority=3; @@ -1543,7 +1543,7 @@ void SetN2kPGN129284(tN2kMsg &N2kMsg, unsigned char SID, double DistanceToWaypoi bool ParseN2kPGN129284(const tN2kMsg &N2kMsg, unsigned char& SID, double& DistanceToWaypoint, tN2kHeadingReference& BearingReference, bool& PerpendicularCrossed, bool& ArrivalCircleEntered, tN2kDistanceCalculationType& CalculationType, double& ETATime, int16_t& ETADate, double& BearingOriginToDestinationWaypoint, double& BearingPositionToDestinationWaypoint, - uint8_t& OriginWaypointNumber, uint8_t& DestinationWaypointNumber, + uint32_t& OriginWaypointNumber, uint32_t& DestinationWaypointNumber, double& DestinationLatitude, double& DestinationLongitude, double& WaypointClosingVelocity) { if(N2kMsg.PGN != 129284L) diff --git a/src/N2kMessages.h b/src/N2kMessages.h index c14d4e9b..2778049b 100644 --- a/src/N2kMessages.h +++ b/src/N2kMessages.h @@ -3877,7 +3877,7 @@ inline bool ParseN2kXTE(const tN2kMsg &N2kMsg, unsigned char& SID, tN2kXTEMode& void SetN2kPGN129284(tN2kMsg &N2kMsg, unsigned char SID, double DistanceToWaypoint, tN2kHeadingReference BearingReference, bool PerpendicularCrossed, bool ArrivalCircleEntered, tN2kDistanceCalculationType CalculationType, double ETATime, int16_t ETADate, double BearingOriginToDestinationWaypoint, double BearingPositionToDestinationWaypoint, - uint8_t OriginWaypointNumber, uint8_t DestinationWaypointNumber, + uint32_t OriginWaypointNumber, uint32_t DestinationWaypointNumber, double DestinationLatitude, double DestinationLongitude, double WaypointClosingVelocity); /************************************************************************//** @@ -3890,7 +3890,7 @@ void SetN2kPGN129284(tN2kMsg &N2kMsg, unsigned char SID, double DistanceToWaypoi inline void SetN2kNavigationInfo(tN2kMsg &N2kMsg, unsigned char SID, double DistanceToWaypoint, tN2kHeadingReference BearingReference, bool PerpendicularCrossed, bool ArrivalCircleEntered, tN2kDistanceCalculationType CalculationType, double ETATime, int16_t ETADate, double BearingOriginToDestinationWaypoint, double BearingPositionToDestinationWaypoint, - uint8_t OriginWaypointNumber, uint8_t DestinationWaypointNumber, + uint32_t OriginWaypointNumber, uint32_t DestinationWaypointNumber, double DestinationLatitude, double DestinationLongitude, double WaypointClosingVelocity) { SetN2kPGN129284(N2kMsg, SID, DistanceToWaypoint, BearingReference, PerpendicularCrossed, ArrivalCircleEntered, CalculationType, @@ -3937,7 +3937,7 @@ inline void SetN2kNavigationInfo(tN2kMsg &N2kMsg, unsigned char SID, double Dist bool ParseN2kPGN129284(const tN2kMsg &N2kMsg, unsigned char& SID, double& DistanceToWaypoint, tN2kHeadingReference& BearingReference, bool& PerpendicularCrossed, bool& ArrivalCircleEntered, tN2kDistanceCalculationType& CalculationType, double& ETATime, int16_t& ETADate, double& BearingOriginToDestinationWaypoint, double& BearingPositionToDestinationWaypoint, - uint8_t& OriginWaypointNumber, uint8_t& DestinationWaypointNumber, + uint32_t& OriginWaypointNumber, uint32_t& DestinationWaypointNumber, double& DestinationLatitude, double& DestinationLongitude, double& WaypointClosingVelocity); @@ -3952,7 +3952,7 @@ bool ParseN2kPGN129284(const tN2kMsg &N2kMsg, unsigned char& SID, double& Distan inline bool ParseN2kNavigationInfo(const tN2kMsg &N2kMsg, unsigned char& SID, double& DistanceToWaypoint, tN2kHeadingReference& BearingReference, bool& PerpendicularCrossed, bool& ArrivalCircleEntered, tN2kDistanceCalculationType& CalculationType, double& ETATime, int16_t& ETADate, double& BearingOriginToDestinationWaypoint, double& BearingPositionToDestinationWaypoint, - uint8_t& OriginWaypointNumber, uint8_t& DestinationWaypointNumber, + uint32_t& OriginWaypointNumber, uint32_t& DestinationWaypointNumber, double& DestinationLatitude, double& DestinationLongitude, double& WaypointClosingVelocity) { return ParseN2kPGN129284(N2kMsg, SID, DistanceToWaypoint, BearingReference, PerpendicularCrossed, ArrivalCircleEntered, CalculationType, ETATime, ETADate, BearingOriginToDestinationWaypoint, BearingPositionToDestinationWaypoint, diff --git a/src/NMEA2000.cpp b/src/NMEA2000.cpp index 84a4c857..2783563b 100644 --- a/src/NMEA2000.cpp +++ b/src/NMEA2000.cpp @@ -422,20 +422,27 @@ bool IsMandatoryFastPacketMessage(unsigned long PGN) { * - 130322L: Current Station Data, pri=6, period=1000 * - 130323L: Meteorological Station Data, pri=6, period=1000 * - 130324L: Moored Buoy Station Data, pri=6, period=1000 - * - 130567L: Watermaker Input Setting and Status, pri=6, period=2500 - * - 130577L: Direction Data PGN, pri=3, period=1000 - * - 130578L: Vessel Speed Components, pri=2, period=250 - * - 130569L: Current File and Status, pri=6, period=500 - * - 130570L: Library Data File, pri=6, period=NA - * - 130571L: Library Data Group, pri=6, period=NA - * - 130572L: Library Data Search, pri=6, period=NA - * - 130573L: Supported Source Data, pri=6, period=NA - * - 130574L: Supported Zone Data, pri=6, period=NA - * - 130580L: System Configuration Status, pri=6, period=NA - * - 130581L: Zone Configuration Status, pri=6, period=NA - * - 130583L: Available Audio EQ Presets, pri=6, period=NA - * - 130584L: Bluetooth Devices, pri=6, period=NA - * - 130586L: Zone Configuration Status, pri=6, period=NA + * - 130330L: Lighting system settings, pri=7, period=NA + * - 130561L: Lighting zone, pri=7, period=NA + * - 130562L: Lighting scene, pri=7, period=NA + * - 130563L: Lighting device, pri=7, period=NA + * - 130564L: Lighting device enumeration, pri=7, period=NA + * - 130565L: Lighting color sequence, pri=7, period=NA + * - 130566L: Lighting program, pri=7, period=NA + * - 130567L: Watermaker input setting and status, pri=6, period=2500 + * - 130577L: Direction data PGN, pri=3, period=1000 + * - 130578L: Vessel speed components, pri=2, period=250 + * - 130569L: Entertainment current file and status, pri=6, period=500 + * - 130570L: Entertainment library data file, pri=6, period=NA + * - 130571L: Entertainment library data group, pri=6, period=NA + * - 130572L: Entertainment library data search, pri=6, period=NA + * - 130573L: Entertainment supported source data, pri=6, period=NA + * - 130574L: Entertainment supported zone data, pri=6, period=NA + * - 130580L: Entertainment system configuration status, pri=6, period=NA + * - 130581L: Entertainment zone configuration status, pri=6, period=NA + * - 130583L: Entertainment available dudio EQ presets, pri=6, period=NA + * - 130584L: Entertainment bluetooth devices, pri=6, period=NA + * - 130586L: Entertainment zone configuration status, pri=6, period=NA * \return false */ bool IsDefaultFastPacketMessage(unsigned long PGN) { @@ -532,6 +539,13 @@ bool IsDefaultFastPacketMessage(unsigned long PGN) { case 130322L: // Current Station Data, pri=6, period=1000 case 130323L: // Meteorological Station Data, pri=6, period=1000 case 130324L: // Moored Buoy Station Data, pri=6, period=1000 + case 130330L: // Lighting system settings, pri=7, period=NA + case 130561L: // Lighting zone, pri=7, period=NA + case 130562L: // Lighting scene, pri=7, period=NA + case 130563L: // Lighting device, pri=7, period=NA + case 130564L: // Lighting device enumeration, pri=7, period=NA + case 130565L: // Lighting color sequence, pri=7, period=NA + case 130566L: // Lighting program, pri=7, period=NA case 130567L: // Watermaker Input Setting and Status, pri=6, period=2500 case 130577L: // Direction Data PGN, pri=3, period=1000 case 130578L: // Vessel Speed Components, pri=2, period=250 @@ -1230,11 +1244,12 @@ bool tNMEA2000::Open() { OpenState=os_Open; StartAddressClaim(); tN2kSyncScheduler::SetSyncOffset(); - if ( OnOpen!=0 ) OnOpen(); #if !defined(N2K_NO_HEARTBEAT_SUPPORT) SetHeartbeatIntervalAndOffset(DefaultHeartbeatInterval,10000); // Init default hearbeat interval and offset. #endif + if ( OnOpen!=0 ) OnOpen(); } else { + // Read rubbish out from CAN controller unsigned long canId; unsigned char len = 0; unsigned char buf[8]; diff --git a/src/NMEA2000.h b/src/NMEA2000.h index 33d3fcc4..4d8632f3 100644 --- a/src/NMEA2000.h +++ b/src/NMEA2000.h @@ -1066,6 +1066,10 @@ class tNMEA2000 * address claming and after it has been accepted, other communication can start. * OnOpen can be used e.g., for message timing synchronization so that every time device starts * messages will have same sent offset. See also SetOnOpen(). + * + * \note In future OnOpen may be called several times, if communication will be reopened + * by \ref Restart or driver error. Developer must take care that possible memory + * initializations will be handled properly in case OnOpen is called several times. */ void (*OnOpen)(); @@ -2480,19 +2484,19 @@ class tNMEA2000 /*********************************************************************//** * \brief Set the Heartbeat Interval and Offset for a device * + * Library will automatically start heartbeat with default interval 60 s + * and offset 10 s. + * * According to document [NMEA Heartbeat Corrigendum](https://web.archive.org/web/20170609023206/https://www.nmea.org/Assets/20140102%20nmea-2000-126993%20heartbeat%20pgn%20corrigendum.pdf) - * all NMEA devices shall transmit heartbeat PGN 126993. With this + * all NMEA devices shall transmit heartbeat PGN 126993. With this * function you can set transmission interval in ms (range 1000-655320ms - * , default 60000). Set <1000 to disable it. - * You can temporally change interval by setting SetAsDefault parameter - * to false. Then you can restore default interval with interval - * parameter value 0xfffffffe + * , default 60000). Set interval 0 to disable heartbeat * * Function allows to set interval over 60 s or 0 to disable sending for test purposes. * - * \param interval Heartbeat Interval in ms - * \param offset Heartbeat Offset in ms - * \param iDev index of the device on \ref Devices + * \param interval Heartbeat Interval in ms. 0xffffffff=keep current, 0xfffffffe=restore default + * \param offset Heartbeat Offset in ms. 0xffffffff=keep current, 0xfffffffe=restore default + * \param iDev Index of the device on \ref Devices or -1 to set for all. */ void SetHeartbeatIntervalAndOffset(uint32_t interval, uint32_t offset=0, int iDev=-1); @@ -2504,7 +2508,7 @@ class tNMEA2000 * startup or not. * * \param iDev index of the device on \ref Devices - * \return uint_32 -> Device heartbeat interval in ms + * \retval uint_32 Device heartbeat interval in ms */ uint32_t GetHeartbeatInterval(int iDev=0) { if (iDev<0 || iDev>=DeviceCount) return 60000; return Devices[iDev].HeartbeatScheduler.GetPeriod(); } /*********************************************************************//** @@ -2515,13 +2519,16 @@ class tNMEA2000 * startup or not. * * \param iDev index of the device on \ref Devices - * \return uint_32 -> Device heartbeat Offset in ms + * \retval uint_32 Device heartbeat Offset in ms */ uint32_t GetHeartbeatOffset(int iDev=0) { if (iDev<0 || iDev>=DeviceCount) return 0; return Devices[iDev].HeartbeatScheduler.GetOffset(); } /*********************************************************************//** * \brief Send heartbeat for specific device. * + * Library will automatically send heartbeat, if interval is >0. You + * can also manually send it any time or force sent, if interval=0; + * * \param iDev index of the device on \ref Devices */ void SendHeartbeat(int iDev); @@ -2536,7 +2543,9 @@ class tNMEA2000 */ void SendHeartbeat(bool force=false); - // deprecated! SetAsDefault has no effect. Use function SetHeartbeatIntervalAndOffset. + /*********************************************************************//** + * \brief Deprecated. Use function \ref SetHeartbeatIntervalAndOffset + */ void SetHeartbeatInterval(unsigned long interval, bool SetAsDefault=true, int iDev=-1) __attribute__ ((deprecated)); #endif @@ -2710,6 +2719,10 @@ class tNMEA2000 * OnOpen will be called, when communication really opens * and starts initial address claiming. You can use this to init your message sending * to syncronize them with e.g., heartbeat. + * + * \note In future OnOpen may be called several times, if communication will be reopened + * by \ref Restart or driver error. Developer must take care that possible memory + * initializations will be handled properly in case OnOpen is called several times. */ void SetOnOpen(void (*_OnOpen)()); diff --git a/src/RingBuffer.h b/src/RingBuffer.h index 8b8997e1..93507cda 100644 --- a/src/RingBuffer.h +++ b/src/RingBuffer.h @@ -25,10 +25,11 @@ * \file RingBuffer.h * \brief Simple tRingBuffer and tPriorityRingBuffer template classes * - * With tRingBuffer one can save simple data structures to a ring buffer. + * With tRingBuffer one can save values to a ring buffer. Value can be + * simple value or data structure. * * \note You have to take care of data locking from other threads - * or interrupts for copy time. + * or interrupts around calls using buffer handling routines. * * \section subSecRBU Example of ring buffer usage * @@ -53,14 +54,14 @@ * \endcode * * As an alternative if you want to avoid data copying twice, you can - * request reference to next data to be saved and copy data directly + * request pointer to next value to be saved and copy data directly * to it. * * \code * void SendMessage() { * tCANData *msgOut; * lockData(); - * if ( (msgOut=CANMessages)!=0 ) { + * if ( (msgOut=CANMessages.getAddRef())!=0 ) { * msgOut->id=1; * msgOut->len=1; * msgOut->data[0]=1; @@ -70,9 +71,10 @@ * \endcode * * tPriorityRingBuffer is similar as tRingBuffer, but it extends - * functionality with item priority. When you add items to buffer, - * you can give them priority and when you read them, highest priority - * item will be read out first. + * functionality with value priority. When you add values to buffer, + * you can give them priority and when during read you can define + * which priority value should be read out or read highest priority + * value. * * ### Debugging * @@ -82,6 +84,7 @@ * \code * #define RING_BUFFER_ERROR_DEBUG * #define RING_BUFFER_DEBUG + * #define RING_BUFFER_INIT_DEBUG * \endcode * * @@ -93,122 +96,159 @@ #include /************************************************************************//** * \class tRingBuffer - * \brief Template Class that holds simple data structures in a ring buffer + * \brief Template Class that holds values in a ring buffer * \ingroup group_coreSupplementary * - * With tRingBuffer one can save simple data structures to a ring buffer. - * - * \sa - * \ref subSecRBU + * With tRingBuffer one can save values to a ring buffer. Value can be + * simple value or data structure. * * \note You have to take care of data locking from other threads - * or interrupts for copy time. + * or interrupts around calls using buffer handling routines. + * + * \sa + * - \ref subSecRBU * * \tparam T Template used for the class */ template class tRingBuffer { protected: - /** \brief Pointer to the buffer in memory*/ + /** \brief Pointer to the ring buffer of values in memory*/ T *buffer; - /** \brief Index of the first data structure in the ring buffer */ + /** \brief Index of the first value in the ring buffer */ uint16_t head; - /** \brief Index of the last data structure in the ring buffer */ + /** \brief Index of the last value in the ring buffer */ uint16_t tail; - /** \brief Number of data structures that can be stored in the ring buffer*/ + /** \brief Number of values that can be stored in the ring buffer*/ uint16_t size; public: /************************************************************************//** - * \brief Construct a new Ring Buffer object + * \brief Construct a new ring buffer object * - * A buffer with the corresponding size is allocated inside the memory. + * A buffer with the corresponding size of values will be allocated to the memory. * * \param _size Size of the ring buffer */ tRingBuffer(uint16_t _size); /************************************************************************//** - * \brief Destroy the Ring Buffer object + * \brief Destroy the ring buffer object * - * All Memory will be freed + * All memory will be freed */ virtual ~tRingBuffer(); /************************************************************************//** - * \brief Get the Size of the ring buffer - * \return uint16_t Size of ring buffer + * \brief Get the size of the ring buffer + * \retval uint16_t size of ring buffer */ uint16_t getSize() const { return size; } /************************************************************************//** * \brief Checks if the ring buffer is empty * - * \return true - * \return false + * \retval true Buffer is empty + * \retval false Buffer has data */ bool isEmpty() const; /************************************************************************//** * \brief Clears the whole ring buffer */ void clear(); /************************************************************************//** - * \brief Returns the number of Entries in the ring buffer - * \return uint16_t Number of entries + * \brief Returns the number of values in the ring buffer + * \retval uint16_t Number of values in the ring buffer */ uint16_t count(); /************************************************************************//** - * \brief Get pointer to next add item if available. Write pointer - * will be incremented. - * \return T* + * \brief Get the pointer to new value added to the ring buffer + * + * Using getAddRef instead of add saves data copying between values. By using + * add, one must first setup value, which will be then copied to buffer + * within add. By using getAddRef one can request pointer to next buffer value and + * copy data directly to it. + * + * \note In multitasking or with interrupts one must take care that buffer + * will no be used before data handling has been finished. This + * differs from add, where it is enough to use locking only around calling add. + * + * \retval "T *" Pointer to added new value + * \retval 0 Buffer is full */ T *getAddRef(); /************************************************************************//** * \brief Adds a new value to the ring buffer * - * \param val Value to be added - * \return true - * \return false -> ring buffer is already full + * \sa + * - \ref getAddRef + * + * \param val Value to be added + * \retval true Add succeeded + * \retval false Buffer is full */ bool add(const T &val); /************************************************************************//** - * \brief Get pointer to next read item if available. Read pointer - * will be inremented. - * \return const T* + * \brief Get the pointer to value read out from the ring buffer + * + * Using getReadRef instead of read saves data copying between values. By using + * read data will be copied to value, which one then may forward + * elsewhere. By using getReadRef one can request pointer to value and + * forward data directly from it. + + * \note In multitasking or with interrupts one must take care that buffer + * will no be used before data handling has been finished. This + * differs from read, where it is enough to use locking only around calling read. + * + * \retval "const T*" Pointer to value read out from the ring buffer + * \retval 0 No values available */ const T *getReadRef(); /************************************************************************//** - * \brief Get pointer to next read item if available. Does not affect - * to read pointer. - * \return const T* + * \brief Get pointer to next value to be read out from the ring buffer. + * Function does not read value out from the ring buffer. + * + * \retval "const T*" Pointer to value, which would be next read out + * from the ring buffer + * \retval 0 No values available, buffer is empty. */ T *peek(); /************************************************************************//** - * \brief Reads a value from the ring buffer + * \brief Reads a value out from the ring buffer * - * \param val Reference to the value that have to be read out - * \return true - * \return false -> There is no data available inside the buffer + * \sa + * - \ref getReadRef + * + * \param val Reference to the value read out from the ring buffer + * \retval true Value read out from buffer. + * Value has been copied to paramater val + * \retval false There is no data available inside the buffer. + * Parameter val will be unchanged. */ bool read(T &val); }; /************************************************************************//** * \class tPriorityRingBuffer - * \brief Template Class that holds simple data structures with given + * \brief Template Class that holds values with given * priorities in a ring buffer * \ingroup group_coreSupplementary * * tPriorityRingBuffer is similar as \ref tRingBuffer, but it extends - * functionality with item priority. When you add items to buffer, - * you can give them priority and when you read them, highest priority - * item will be read out first. - * \sa - * \ref subSecRBU + * functionality with value priority. When you add values to the buffer, + * you can give them priority. When you read them out you can get highest + * priority or request specific priority value. + * + * The advantage of tPriorityRingBuffer is that it will reserve one + * continuous block for all priorities instead of reserve one buffer for each + * priority. * * \note You have to take care of data locking from other threads - * or interrupts for copy time. + * or interrupts around calls using buffer handling routines. + * + * \sa + * \ref subSecRBU * * \tparam T Template used for the class */ @@ -250,39 +290,60 @@ template class tPriorityRingBuffer { void clear() { next=INVALID_RING_REF; last=INVALID_RING_REF; } }; protected: + /** \brief Pointer to the ring puffer in memory */ + tValueSlot *buffer; + /** \brief Pointer to priority referencies in buffer */ + tPriorityRef *priorityReferencies; /** \brief Index of the first free index in the ring buffer */ uint16_t head; /** \brief Index of the last occupied index in the ring buffer */ uint16_t tail; - /** \brief Number of data structures that can be stored in the ring buffer*/ + /** \brief Number of values that can be stored in the ring buffer*/ uint16_t size; /** \brief highest priority possible*/ uint8_t maxPriorities; - /** \brief Pointer to the ring puffer in memory*/ - tValueSlot *buffer; - /** \brief */ - tPriorityRef *priorityReferencies; public: /************************************************************************//** - * \brief Construct a new Priority Ring Buffer object + * \brief Construct a new Priority ring buffer object * - * Allocates all the memory needed and initialize attributes and priorities. + * Allocates all the memory needed, initialize attributes and priorities. * * \param _size Size of the ring buffer * \param _maxPriorities highest priority possible */ tPriorityRingBuffer(uint16_t _size, uint8_t _maxPriorities=1); /************************************************************************//** - * \brief Destroy the t Priority Ring Buffer object + * \brief Destroy the priority ring buffer object * Frees all memory */ virtual ~tPriorityRingBuffer(); + /************************************************************************//** + * \brief Get the size of the ring buffer + * \retval uint16_t Size of ring buffer + */ uint16_t getSize() const { return size; } /************************************************************************//** - * \brief Get the Size of the ring buffer - * \return uint16_t Size of ring buffer + * \brief Get the size of memory required by ring buffer + * + * This is static function and can be used to test how much ring buffer + * would require memory with given size and count of priorities. + * Function can be used e.g., for checking available memory or possible + * memory "bank" handling. + * + * \return uint32_t Memory required by ring buffer + */ + static uint32_t getMemSize(uint16_t _size, uint8_t _maxPriorities=1); + /************************************************************************//** + * \brief Check is ring buffer empty + * + * Result depends of _priority parameter. If valid priority within + * initial _maxPriorities is provided, function returns empty state of + * given priority. Otherwise it returns empty state of whole buffer. + * + * \param _priority Which buffer + * \retval bool Buffer empty state */ bool isEmpty(uint8_t _priority=0xff) const; /************************************************************************//** @@ -292,49 +353,92 @@ template class tPriorityRingBuffer { * \brief This function does nothing at the moment!*/ void clean(); /************************************************************************//** - * \brief Returns the number of Entries in the ring buffer - * \return uint16_t Number of entries + * \brief Returns the number of values in the ring buffer + * + * Currently function returns simple buffer count instead of true value + * count. Priority ring buffer may have values released between other values. + * + * \retval uint16_t Number of values in buffer */ uint16_t count(); /************************************************************************//** * \brief Add a value to the ring buffer with a given priority * + * \sa + * - \ref getAddRef + * * \param val Value to be added * \param _priority Priority for the added value (default = 0) - * \return true - * \return false -> ring buffer is already full + * \retval true Add succeeded + * \retval false Buffer is full */ bool add(const T &val, uint8_t _priority=0); /************************************************************************//** - * \brief Reads a value from the ring buffer + * \brief Reads highest priority value out from the ring buffer * - * \param val Reference to the value that have to be read out - * \return true - * \return false -> There is no data available inside the buffer + * \sa + * - \ref getReadRef + * + * \param val Reference to the value read out from buffer + * \retval true Value read out from buffer. + * Value has been copied to paramater val + * \retval false There is no data available inside the buffer. + * Parameter val will be unchanged. */ bool read(T &val); /************************************************************************//** - * \brief Get the Reference for the next Add + * \brief Get the pointer to new value added to the ring buffer * + * Using getAddRef instead of add saves data copying between values. By using + * add, one must first setup value, which will be then copied to buffer + * within add. By using getAddRef one can request pointer to next buffer value and + * copy data directly to it. + * + * \note In multitasking or with interrupts one must take care that buffer + * will no be used before data handling has been finished. This + * differs from add, where it is enough to use locking only around calling add. + * * \param _priority Priority for the value (default = 0) - * \return T* -> Null, if buffer is full + * \retval "T *" Pointer to added new value + * \retval 0 Buffer is full */ T *getAddRef(uint8_t _priority=0); /************************************************************************//** - * \brief Get the Reference for the next Read + * \brief Get the pointer to value with given priority read out from the ring buffer * - * \param _priority Priority for the value - * \return const T* + * Using getReadRef instead of read saves data copying between values. By using + * read data will be copied to value, which one then may forward + * elsewhere. By using getReadRef one can request pointer to value and + * forward data directly from it. + + * \note In multitasking or with interrupts one must take care that buffer + * will no be used before data handling has been finished. This + * differs from read, where it is enough to use locking only around calling read. + * + * \param _priority Priority for the value read out + * \retval "const T*" Pointer to value read out from the ring buffer + * \retval 0 No values available with given priority */ const T *getReadRef(uint8_t _priority); /************************************************************************//** - * \brief Get reference to highest available. + * \brief Get pointer to highest priority value read out from the the ring buffer. * - * \param *_priority Priority for the value (default = 0) - * \return const T* + * Using getReadRef instead of read saves data copying between values. By using + * read data will be copied to value, which one then may forward + * elsewhere. By using getReadRef one can request pointer to value and + * forward data directly from it. + + * \note In multitasking or with interrupts one must take care that buffer + * will no be used before data handling has been finished. This + * differs from read, where it is enough to use locking only around calling read. + * + * \param *_priority Pointer to priority, which will be set to priority + * of the value readed out. + * \retval "const T*" Pointer to value read out from the ring buffer + * \retval 0 No values available. */ const T *getReadRef(uint8_t *_priority=0); }; diff --git a/src/RingBuffer.tpp b/src/RingBuffer.tpp index f28cdba8..0a58fabe 100644 --- a/src/RingBuffer.tpp +++ b/src/RingBuffer.tpp @@ -29,8 +29,9 @@ //#define RING_BUFFER_ERROR_DEBUG //#define RING_BUFFER_DEBUG +//#define RING_BUFFER_INIT_DEBUG -#if defined(RING_BUFFER_DEBUG) || defined(RING_BUFFER_ERROR_DEBUG) +#if defined(RING_BUFFER_DEBUG) || defined(RING_BUFFER_ERROR_DEBUG) || defined(RING_BUFFER_INIT_DEBUG) #include #ifndef DebugStream #define DebugStream Serial @@ -49,6 +50,12 @@ # define RingBufferDbgf(fmt, args...) #endif +#if defined(RING_BUFFER_INIT_DEBUG) + # define RingBufferInitDbgf(fmt, args...) DebugStream.printf (fmt , ## args) +#else + # define RingBufferInitDbgf(fmt, args...) +#endif + // ***************************************************************************** template @@ -94,7 +101,7 @@ bool tRingBuffer::add(const T &val) { if ( nextEntry == tail ) return false; // Add the element to the ring - memcpy((void *)&(buffer[head]), (void *)&val, sizeof (T)); + memcpy(&(buffer[head]), &val, sizeof(T)); // Bump the head to point to the next free entry head = nextEntry; @@ -109,7 +116,7 @@ bool tRingBuffer::read(T &val) { if ( isEmpty() ) return false; // copy the message - memcpy ((void *)&val, (void *)&buffer[tail], sizeof (T)); + memcpy(&val, &buffer[tail], sizeof(T)); // Bump the tail pointer tail = (tail + 1) % size; @@ -160,6 +167,8 @@ T *tRingBuffer::peek() { return ret; } +#define INVALID_PRIORITY 0xff + //============================================================================== // class tPriorityRingBuffer @@ -169,9 +178,13 @@ tPriorityRingBuffer::tPriorityRingBuffer(uint16_t _size, uint8_t _maxPrioriti head(0), tail(0), size(_size), maxPriorities(_maxPriorities) { if ( size<3 ) size=3; if ( maxPriorities<1 ) maxPriorities=1; + if ( maxPriorities==255 ) maxPriorities=254; + RingBufferInitDbgf("tPriorityRingBuffer::tPriorityRingBuffer, size:%u, priorities:%u\n",size,maxPriorities); + RingBufferInitDbgf(" - size:%u, priorities:%u, memory required: %u\n",size,maxPriorities,size*sizeof(tValueSlot)+maxPriorities*sizeof(tPriorityRef)); buffer=new tValueSlot[size]; + RingBufferInitDbgf(" - ring buffer allocated to %x\n",(uint32_t)buffer); priorityReferencies=new tPriorityRef[maxPriorities]; - RingBufferDbgf("tPriorityRingBuffer::tPriorityRingBuffer, ring buffer initialized size:%u, priorities:%u\n",size,maxPriorities); + RingBufferInitDbgf(" - priorityReferencies allocated to %x\n",(uint32_t)priorityReferencies); } // ***************************************************************************** @@ -181,6 +194,16 @@ tPriorityRingBuffer::~tPriorityRingBuffer() { delete[] priorityReferencies; } +// ***************************************************************************** +template +uint32_t tPriorityRingBuffer::getMemSize(uint16_t _size, uint8_t _maxPriorities) { + if ( _size<3 ) _size=3; + if ( _maxPriorities<1 ) _maxPriorities=1; + if ( _maxPriorities==255 ) _maxPriorities=254; + + return sizeof(::tPriorityRingBuffer)+_size*sizeof(tValueSlot)+_maxPriorities*sizeof(tPriorityRef); +} + // ***************************************************************************** template bool tPriorityRingBuffer::isEmpty(uint8_t _priority) const { @@ -203,6 +226,16 @@ template void tPriorityRingBuffer::clean() { } +// ***************************************************************************** +template +uint16_t tPriorityRingBuffer::count() { + int32_t entries = head - tail; + + if ( entries < 0 ) entries += size; + + return (uint16_t)entries; +} + // ***************************************************************************** template bool tPriorityRingBuffer::add(const T &val, uint8_t _priority) { @@ -284,9 +317,12 @@ const T *tPriorityRingBuffer::getReadRef(uint8_t _priority) { } // Update tail, if we are removing first if ( ref==tail ) { - for ( tail = (tail + 1) % size; tail!=head && buffer[tail].next==INVALID_RING_REF; tail = (tail + 1) % size ); + for ( tail = (tail + 1) % size; + tail!=head && buffer[tail].priority==INVALID_PRIORITY; + tail = (tail + 1) % size ); } buffer[ref].next=INVALID_RING_REF; // Release slot + buffer[ref].priority=INVALID_PRIORITY; RingBufferDbgf("tPriorityRingBuffer::getReadRef, read item ref:%u, priority:%u, tail:%u\n",ref,_priority,tail);