tcq.c
1 /*! 2 ******************************************************************** 3 * Description: tcq.c 4 *\brief queue handling functions for trajectory planner 5 * These following functions implement the motion queue that 6 * is fed by tpAddLine/tpAddCircle and consumed by tpRunCycle. 7 * They have been fully working for a long time and a wise programmer 8 * won't mess with them. 9 * 10 *\author Derived from a work by Fred Proctor & Will Shackleford 11 *\author rewritten by Chris Radek 12 * 13 * License: GPL Version 2 14 * System: Linux 15 * 16 * Copyright (c) 2004 All rights reserved. 17 * 18 ********************************************************************/ 19 20 #include "tcq.h" 21 #include <stddef.h> 22 23 /** Return 0 if queue is valid, -1 if not */ 24 static inline int tcqCheck(TC_QUEUE_STRUCT const * const tcq) 25 { 26 if ((0 == tcq) || (0 == tcq->queue)) 27 { 28 return -1; 29 } 30 return 0; 31 } 32 33 /*! tcqCreate() function 34 * 35 * \brief Creates a new queue for TC elements. 36 * 37 * This function creates a new queue for TC elements. 38 * It gets called by tpCreate() 39 * 40 * @param tcq pointer to the new TC_QUEUE_STRUCT 41 * @param _size size of the new queue 42 * @param tcSpace holds the space allocated for the new queue, allocated in motion.c 43 * 44 * @return int returns success or failure 45 */ 46 int tcqCreate(TC_QUEUE_STRUCT * const tcq, int _size, TC_STRUCT * const tcSpace) 47 { 48 if (!tcq || !tcSpace || _size < 1) { 49 return -1; 50 } 51 tcq->queue = tcSpace; 52 tcq->size = _size; 53 tcqInit(tcq); 54 55 return 0; 56 } 57 58 /*! tcqDelete() function 59 * 60 * \brief Deletes a queue holding TC elements. 61 * 62 * This function creates deletes a queue. It doesn't free the space 63 * only throws the pointer away. 64 * It gets called by tpDelete() 65 * \todo FIXME, it seems tpDelete() is gone, and this function isn't used. 66 * 67 * @param tcq pointer to the TC_QUEUE_STRUCT 68 * 69 * @return int returns success 70 */ 71 int tcqDelete(TC_QUEUE_STRUCT * const tcq) 72 { 73 if (!tcqCheck(tcq)) { 74 /* free(tcq->queue); */ 75 tcq->queue = 0; 76 } 77 78 return 0; 79 } 80 81 /*! tcqInit() function 82 * 83 * \brief Initializes a queue with TC elements. 84 * 85 * This function initializes a queue with TC elements. 86 * It gets called by tpClear() and 87 * by tpRunCycle() when we are aborting 88 * 89 * @param tcq pointer to the TC_QUEUE_STRUCT 90 * 91 * @return int returns success or failure (if no tcq found) 92 */ 93 int tcqInit(TC_QUEUE_STRUCT * const tcq) 94 { 95 if (tcqCheck(tcq)) return -1; 96 97 tcq->_len = 0; 98 tcq->start = tcq->end = 0; 99 tcq->rend = 0; 100 tcq->_rlen = 0; 101 tcq->allFull = 0; 102 103 return 0; 104 } 105 106 /*! tcqPut() function 107 * 108 * \brief puts a TC element at the end of the queue 109 * 110 * This function adds a tc element at the end of the queue. 111 * It gets called by tpAddLine() and tpAddCircle() 112 * 113 * @param tcq pointer to the new TC_QUEUE_STRUCT 114 * @param tc the new TC element to be added 115 * 116 * @return int returns success or failure 117 */ 118 int tcqPut(TC_QUEUE_STRUCT * const tcq, TC_STRUCT const * const tc) 119 { 120 /* check for initialized */ 121 if (tcqCheck(tcq)) return -1; 122 123 /* check for allFull, so we don't overflow the queue */ 124 if (tcq->allFull) { 125 return -1; 126 } 127 128 /* add it */ 129 tcq->queue[tcq->end] = *tc; 130 tcq->_len++; 131 132 /* update end ptr, modulo size of queue */ 133 tcq->end = (tcq->end + 1) % tcq->size; 134 135 /* set allFull flag if we're really full */ 136 if (tcq->end == tcq->start) { 137 tcq->allFull = 1; 138 } 139 140 return 0; 141 } 142 143 144 /*! tcqPopBack() function 145 * 146 * \brief removes the newest TC element (converse of tcqRemove) 147 * 148 * @param tcq pointer to the TC_QUEUE_STRUCT 149 * 150 * @return int returns success or failure 151 */ 152 int tcqPopBack(TC_QUEUE_STRUCT * const tcq) 153 { 154 /* check for initialized */ 155 if (tcqCheck(tcq)) return -1; 156 157 /* Too short to pop! */ 158 if (tcq->_len < 1) { 159 return -1; 160 } 161 162 int n = tcq->end - 1 + tcq->size; 163 tcq->end = n % tcq->size; 164 tcq->_len--; 165 166 return 0; 167 } 168 169 #define TCQ_REVERSE_MARGIN 200 170 171 int tcqPop(TC_QUEUE_STRUCT * const tcq) 172 { 173 174 if (tcqCheck(tcq)) { 175 return -1; 176 } 177 178 if (tcq->_len < 1 && !tcq->allFull) { 179 return -1; 180 } 181 182 /* update start ptr and reset allFull flag and len */ 183 tcq->start = (tcq->start + 1) % tcq->size; 184 tcq->allFull = 0; 185 tcq->_len--; 186 187 if (tcq->_rlen < TCQ_REVERSE_MARGIN) { 188 //If we're not overwriting the history yet, then we have another segment added to the reverse history 189 tcq->_rlen++; 190 } else { 191 //If we're run out of spare reverse history, then advance rend 192 tcq->rend = (tcq->rend + 1) % tcq->size; 193 } 194 195 return 0; 196 } 197 198 /*! tcqRemove() function 199 * 200 * \brief removes n items from the queue 201 * 202 * This function removes the first n items from the queue, 203 * after checking that they can be removed 204 * (queue initialized, queue not empty, enough elements in it) 205 * Function gets called by tpRunCycle() with n=1 206 * \todo FIXME: Optimize the code to remove only 1 element, might speed it up 207 * 208 * @param tcq pointer to the new TC_QUEUE_STRUCT 209 * @param n the number of TC elements to be removed 210 * 211 * @return int returns success or failure 212 */ 213 int tcqRemove(TC_QUEUE_STRUCT * const tcq, int n) 214 { 215 216 if (n <= 0) { 217 return 0; /* okay to remove 0 or fewer */ 218 } 219 220 if (tcqCheck(tcq) || ((tcq->start == tcq->end) && !tcq->allFull) || 221 (n > tcq->_len)) { /* too many requested */ 222 return -1; 223 } 224 225 /* update start ptr and reset allFull flag and len */ 226 tcq->start = (tcq->start + n) % tcq->size; 227 tcq->allFull = 0; 228 tcq->_len -= n; 229 230 return 0; 231 } 232 233 234 /** 235 * Step backward into the reverse history. 236 */ 237 int tcqBackStep(TC_QUEUE_STRUCT * const tcq) 238 { 239 240 if (tcqCheck(tcq)) { 241 return -1; 242 } 243 244 // start == end means that queue is empty 245 246 if ( tcq->start == tcq->rend) { 247 return -1; 248 } 249 /* update start ptr and reset allFull flag and len */ 250 tcq->start = (tcq->start - 1 + tcq->size) % tcq->size; 251 tcq->_len++; 252 tcq->_rlen--; 253 254 return 0; 255 } 256 257 /*! tcqLen() function 258 * 259 * \brief returns the number of elements in the queue 260 * 261 * Function gets called by tpSetVScale(), tpAddLine(), tpAddCircle() 262 * 263 * @param tcq pointer to the TC_QUEUE_STRUCT 264 * 265 * @return int returns number of elements 266 */ 267 int tcqLen(TC_QUEUE_STRUCT const * const tcq) 268 { 269 if (tcqCheck(tcq)) return -1; 270 271 return tcq->_len; 272 } 273 274 /*! tcqItem() function 275 * 276 * \brief gets the n-th TC element in the queue, without removing it 277 * 278 * Function gets called by tpSetVScale(), tpRunCycle(), tpIsPaused() 279 * 280 * @param tcq pointer to the TC_QUEUE_STRUCT 281 * 282 * @return TC_STRUCT returns the TC elements 283 */ 284 TC_STRUCT * tcqItem(TC_QUEUE_STRUCT const * const tcq, int n) 285 { 286 if (tcqCheck(tcq) || (n < 0) || (n >= tcq->_len)) return NULL; 287 288 return &(tcq->queue[(tcq->start + n) % tcq->size]); 289 } 290 291 /*! 292 * \def TC_QUEUE_MARGIN 293 * sets up a margin at the end of the queue, to reduce effects of race conditions 294 */ 295 #define TC_QUEUE_MARGIN (TCQ_REVERSE_MARGIN+20) 296 297 /*! tcqFull() function 298 * 299 * \brief get the full status of the queue 300 * Function returns full if the count is closer to the end of the queue than TC_QUEUE_MARGIN 301 * 302 * Function called by update_status() in control.c 303 * 304 * @param tcq pointer to the TC_QUEUE_STRUCT 305 * 306 * @return int returns status (0==not full, 1==full) 307 */ 308 int tcqFull(TC_QUEUE_STRUCT const * const tcq) 309 { 310 if (tcqCheck(tcq)) { 311 return 1; /* null queue is full, for safety */ 312 } 313 314 /* call the queue full if the length is into the margin, so reduce the 315 effect of a race condition where the appending process may not see the 316 full status immediately and send another motion */ 317 318 if (tcq->size <= TC_QUEUE_MARGIN) { 319 /* no margin available, so full means really all full */ 320 return tcq->allFull; 321 } 322 323 if (tcq->_len >= tcq->size - TC_QUEUE_MARGIN) { 324 /* we're into the margin, so call it full */ 325 return 1; 326 } 327 328 /* we're not into the margin */ 329 return 0; 330 } 331 332 /*! tcqLast() function 333 * 334 * \brief gets the last TC element in the queue, without removing it 335 * 336 * 337 * @param tcq pointer to the TC_QUEUE_STRUCT 338 * 339 * @return TC_STRUCT returns the TC element 340 */ 341 TC_STRUCT *tcqLast(TC_QUEUE_STRUCT const * const tcq) 342 { 343 if (tcqCheck(tcq)) { 344 return NULL; 345 } 346 if (tcq->_len == 0) { 347 return NULL; 348 } 349 //Fix for negative modulus error 350 int n = tcq->end-1 + tcq->size; 351 return &(tcq->queue[n % tcq->size]); 352 353 } 354