CO_trace.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. /*
  2. * CANopen trace interface.
  3. *
  4. * @file CO_trace.c
  5. * @author Janez Paternoster
  6. * @copyright 2016 - 2020 Janez Paternoster
  7. *
  8. * This file is part of CANopenNode, an opensource CANopen Stack.
  9. * Project home page is <https://github.com/CANopenNode/CANopenNode>.
  10. * For more information on CANopen see <http://www.can-cia.org/>.
  11. *
  12. * Licensed under the Apache License, Version 2.0 (the "License");
  13. * you may not use this file except in compliance with the License.
  14. * You may obtain a copy of the License at
  15. *
  16. * http://www.apache.org/licenses/LICENSE-2.0
  17. *
  18. * Unless required by applicable law or agreed to in writing, software
  19. * distributed under the License is distributed on an "AS IS" BASIS,
  20. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  21. * See the License for the specific language governing permissions and
  22. * limitations under the License.
  23. */
  24. #include "extra/CO_trace.h"
  25. #if (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE
  26. #include <stdio.h>
  27. #include <string.h>
  28. #if !((CO_CONFIG_TRACE) & CO_CONFIG_TRACE_OWN_INTTYPES)
  29. #include <inttypes.h> /* for PRIu32("u" or "lu") and PRId32("d" or "ld") */
  30. #endif
  31. /* Different functions for processing value for different data types. */
  32. static int32_t getValueI8 (void *OD_variable) { return (int32_t) *((int8_t*) OD_variable);}
  33. static int32_t getValueI16(void *OD_variable) { return (int32_t) *((int16_t*) OD_variable);}
  34. static int32_t getValueI32(void *OD_variable) { return *((int32_t*) OD_variable);}
  35. static int32_t getValueU8 (void *OD_variable) { return (int32_t) *((uint8_t*) OD_variable);}
  36. static int32_t getValueU16(void *OD_variable) { return (int32_t) *((uint16_t*) OD_variable);}
  37. static int32_t getValueU32(void *OD_variable) { return *((int32_t*) OD_variable);}
  38. /* Different functions for printing points for different data types. */
  39. static uint32_t printPointCsv(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
  40. return snprintf(s, size, "%" PRIu32 ";%" PRId32 "\n", timeStamp, value);
  41. }
  42. static uint32_t printPointCsvUnsigned(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
  43. return snprintf(s, size, "%" PRIu32 ";%" PRIu32 "\n", timeStamp, (uint32_t) value);
  44. }
  45. static uint32_t printPointBinary(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
  46. if (size < 8) return 0;
  47. uint32_t timeStampSw = CO_SWAP_32(timeStamp);
  48. int32_t valueSw = CO_SWAP_32(value);
  49. memcpy(s, &timeStampSw, sizeof(timeStampSw));
  50. memcpy(s+4, &valueSw, sizeof(valueSw));
  51. return 8;
  52. }
  53. static uint32_t printPointSvgStart(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
  54. return snprintf(s, size, "M%" PRIu32 ",%" PRId32, timeStamp, value);
  55. }
  56. static uint32_t printPointSvgStartUnsigned(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
  57. return snprintf(s, size, "M%" PRIu32 ",%" PRIu32, timeStamp, (uint32_t) value);
  58. }
  59. static uint32_t printPointSvg(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
  60. return snprintf(s, size, "H%" PRIu32 "V%" PRId32, timeStamp, value);
  61. }
  62. static uint32_t printPointSvgUnsigned(char *s, uint32_t size, uint32_t timeStamp, int32_t value) {
  63. return snprintf(s, size, "H%" PRIu32 "V%" PRIu32, timeStamp, (uint32_t) value);
  64. }
  65. /* Collection of function pointers for fast processing based on specific data type. */
  66. /* Rules for the array: There must be groups of six members (I8, I16, I32, U8, U16, U32)
  67. * in correct order and sequence, so findVariable() finds correct member. */
  68. static const CO_trace_dataType_t dataTypes[] = {
  69. {getValueI8, printPointCsv, printPointCsv, printPointCsv},
  70. {getValueI16, printPointCsv, printPointCsv, printPointCsv},
  71. {getValueI32, printPointCsv, printPointCsv, printPointCsv},
  72. {getValueU8, printPointCsvUnsigned, printPointCsvUnsigned, printPointCsvUnsigned},
  73. {getValueU16, printPointCsvUnsigned, printPointCsvUnsigned, printPointCsvUnsigned},
  74. {getValueU32, printPointCsvUnsigned, printPointCsvUnsigned, printPointCsvUnsigned},
  75. {getValueI8, printPointBinary, printPointBinary, printPointBinary},
  76. {getValueI16, printPointBinary, printPointBinary, printPointBinary},
  77. {getValueI32, printPointBinary, printPointBinary, printPointBinary},
  78. {getValueU8, printPointBinary, printPointBinary, printPointBinary},
  79. {getValueU16, printPointBinary, printPointBinary, printPointBinary},
  80. {getValueU32, printPointBinary, printPointBinary, printPointBinary},
  81. {getValueI8, printPointSvgStart, printPointSvg, printPointSvg},
  82. {getValueI16, printPointSvgStart, printPointSvg, printPointSvg},
  83. {getValueI32, printPointSvgStart, printPointSvg, printPointSvg},
  84. {getValueU8, printPointSvgStartUnsigned, printPointSvgUnsigned, printPointSvgUnsigned},
  85. {getValueU16, printPointSvgStartUnsigned, printPointSvgUnsigned, printPointSvgUnsigned},
  86. {getValueU32, printPointSvgStartUnsigned, printPointSvgUnsigned, printPointSvgUnsigned}
  87. };
  88. /* Find variable in Object Dictionary *****************************************/
  89. static void findVariable(CO_trace_t *trace) {
  90. bool_t err = false;
  91. uint16_t index;
  92. uint8_t subIndex;
  93. uint8_t dataLen;
  94. void *OdDataPtr = NULL;
  95. unsigned dtIndex = 0;
  96. /* parse mapping */
  97. index = (uint16_t) ((*trace->map) >> 16);
  98. subIndex = (uint8_t) ((*trace->map) >> 8);
  99. dataLen = (uint8_t) (*trace->map);
  100. if ((dataLen & 0x07) != 0) { /* data length must be byte aligned */
  101. err = true;
  102. }
  103. dataLen >>= 3; /* in bytes now */
  104. if (dataLen == 0) {
  105. dataLen = 4;
  106. }
  107. /* find mapped variable, if map available */
  108. if (!err && (index != 0 || subIndex != 0)) {
  109. uint16_t entryNo = CO_OD_find(trace->SDO, index);
  110. if (index >= 0x1000 && entryNo != 0xFFFF && subIndex <= trace->SDO->OD[entryNo].maxSubIndex) {
  111. OdDataPtr = CO_OD_getDataPointer(trace->SDO, entryNo, subIndex);
  112. }
  113. if (OdDataPtr != NULL) {
  114. uint16_t len = CO_OD_getLength(trace->SDO, entryNo, subIndex);
  115. if (len < dataLen) {
  116. dataLen = len;
  117. }
  118. }
  119. else {
  120. err = true;
  121. }
  122. }
  123. /* Get function pointers for correct data type */
  124. if (!err) {
  125. /* first sequence: data length */
  126. switch(dataLen) {
  127. case 1: dtIndex = 0; break;
  128. case 2: dtIndex = 1; break;
  129. case 4: dtIndex = 2; break;
  130. default: err = true; break;
  131. }
  132. /* second sequence: signed or unsigned */
  133. if (((*trace->format) & 1) == 1) {
  134. dtIndex += 3;
  135. }
  136. /* third sequence: Output type */
  137. dtIndex += ((*trace->format) >> 1) * 6;
  138. if (dtIndex > (sizeof(dataTypes) / sizeof(CO_trace_dataType_t))) {
  139. err = true;
  140. }
  141. }
  142. /* set output variables */
  143. if (!err) {
  144. if (OdDataPtr != NULL) {
  145. trace->OD_variable = OdDataPtr;
  146. }
  147. else {
  148. trace->OD_variable = trace->value;
  149. }
  150. trace->dt = &dataTypes[dtIndex];
  151. }
  152. else {
  153. trace->OD_variable = NULL;
  154. trace->dt = NULL;
  155. }
  156. }
  157. /* OD function for accessing _OD_traceConfig_ (index 0x2300+) from SDO server.
  158. * For more information see file CO_SDOserver.h. */
  159. static CO_SDO_abortCode_t CO_ODF_traceConfig(CO_ODF_arg_t *ODF_arg) {
  160. CO_trace_t *trace;
  161. CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
  162. trace = (CO_trace_t*) ODF_arg->object;
  163. switch(ODF_arg->subIndex) {
  164. case 1: /* size */
  165. if (ODF_arg->reading) {
  166. CO_setUint32(ODF_arg->data, trace->bufferSize);
  167. }
  168. break;
  169. case 2: /* axisNo (trace enabled if nonzero) */
  170. if (ODF_arg->reading) {
  171. uint8_t *value = (uint8_t*) ODF_arg->data;
  172. if (!trace->enabled) {
  173. *value = 0;
  174. }
  175. }
  176. else {
  177. uint8_t *value = (uint8_t*) ODF_arg->data;
  178. if (*value == 0) {
  179. trace->enabled = false;
  180. }
  181. else if (!trace->enabled) {
  182. if (trace->bufferSize == 0) {
  183. ret = CO_SDO_AB_OUT_OF_MEM;
  184. }
  185. else {
  186. /* set trace->OD_variable and trace->dt, based on 'map' and 'format' */
  187. findVariable(trace);
  188. if (trace->OD_variable != NULL) {
  189. *trace->value = 0;
  190. *trace->minValue = 0;
  191. *trace->maxValue = 0;
  192. *trace->triggerTime = 0;
  193. trace->valuePrev = 0;
  194. trace->readPtr = 0;
  195. trace->writePtr = 0;
  196. trace->enabled = true;
  197. }
  198. else {
  199. ret = CO_SDO_AB_NO_MAP;
  200. }
  201. }
  202. }
  203. }
  204. break;
  205. case 5: /* map */
  206. case 6: /* format */
  207. if (!ODF_arg->reading) {
  208. if (trace->enabled) {
  209. ret = CO_SDO_AB_INVALID_VALUE;
  210. }
  211. }
  212. break;
  213. }
  214. return ret;
  215. }
  216. /* OD function for accessing _OD_trace_ (index 0x2400+) from SDO server.
  217. * For more information see file CO_SDOserver.h. */
  218. static CO_SDO_abortCode_t CO_ODF_trace(CO_ODF_arg_t *ODF_arg) {
  219. CO_trace_t *trace;
  220. CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
  221. trace = (CO_trace_t*) ODF_arg->object;
  222. switch(ODF_arg->subIndex) {
  223. case 1: /* size */
  224. if (ODF_arg->reading) {
  225. uint32_t size = trace->bufferSize;
  226. uint32_t wp = trace->writePtr;
  227. uint32_t rp = trace->readPtr;
  228. if (wp >= rp) {
  229. CO_setUint32(ODF_arg->data, wp - rp);
  230. }
  231. else {
  232. CO_setUint32(ODF_arg->data, size - rp + wp);
  233. }
  234. }
  235. else {
  236. if (CO_getUint32(ODF_arg->data) == 0) {
  237. /* clear buffer, handle race conditions */
  238. while(trace->readPtr != 0 || trace->writePtr != 0) {
  239. trace->readPtr = 0;
  240. trace->writePtr = 0;
  241. *trace->triggerTime = 0;
  242. }
  243. }
  244. else {
  245. ret = CO_SDO_AB_INVALID_VALUE;
  246. }
  247. }
  248. break;
  249. case 5: /* plot */
  250. if (ODF_arg->reading) {
  251. /* This plot will be transmitted as domain data type. String data
  252. * will be printed directly to SDO buffer. If there is more data
  253. * to print, than is the size of SDO buffer, then this function
  254. * will be called multiple times until internal trace buffer is
  255. * empty. Internal trace buffer is circular buffer. It is accessed
  256. * by this function and by higher priority thread. If this buffer
  257. * is full, there is a danger for race condition. First records
  258. * from trace buffer may be overwritten somewhere between. If this
  259. * is detected, then do{}while() loop tries printing again. */
  260. if (trace->bufferSize == 0 || ODF_arg->dataLength < 100) {
  261. ret = CO_SDO_AB_OUT_OF_MEM;
  262. }
  263. else if (trace->readPtr == trace->writePtr) {
  264. ret = CO_SDO_AB_NO_DATA;
  265. }
  266. else {
  267. uint32_t rp, t, v, len, freeLen;
  268. char *s;
  269. bool_t readPtrOverflowed; /* for handling race conditions */
  270. /* repeat everything, if trace->readPtr was overflowed in CO_trace_process */
  271. do {
  272. readPtrOverflowed = false;
  273. s = (char*) ODF_arg->data;
  274. freeLen = ODF_arg->dataLength;
  275. rp = trace->readPtr;
  276. /* start plot, increment variables, verify overflow */
  277. if (ODF_arg->firstSegment) {
  278. t = trace->timeBuffer[rp];
  279. v = trace->valueBuffer[rp];
  280. rp ++;
  281. if (++trace->readPtr == trace->bufferSize) {
  282. trace->readPtr = 0;
  283. if (rp != trace->bufferSize) {
  284. readPtrOverflowed = true;
  285. continue;
  286. }
  287. rp = 0;
  288. }
  289. if (rp != trace->readPtr) {
  290. readPtrOverflowed = true;
  291. continue;
  292. }
  293. len = trace->dt->printPointStart(s, freeLen, t, v);
  294. s += len;
  295. freeLen -= len;
  296. }
  297. /* print other points */
  298. if (rp != trace->writePtr) {
  299. for(;;) {
  300. t = trace->timeBuffer[rp];
  301. v = trace->valueBuffer[rp];
  302. rp ++;
  303. if (++trace->readPtr == trace->bufferSize) {
  304. trace->readPtr = 0;
  305. if (rp != trace->bufferSize && ODF_arg->firstSegment) {
  306. readPtrOverflowed = true;
  307. break;
  308. }
  309. rp = 0;
  310. }
  311. if (rp != trace->readPtr && ODF_arg->firstSegment) {
  312. readPtrOverflowed = true;
  313. break;
  314. }
  315. /* If internal buffer is empty, end transfer */
  316. if (rp == trace->writePtr) {
  317. /* If there is last time stamp, point will be printed at the end */
  318. if (t != trace->lastTimeStamp) {
  319. len = trace->dt->printPoint(s, freeLen, t, v);
  320. s += len;
  321. freeLen -= len;
  322. }
  323. ODF_arg->lastSegment = true;
  324. break;
  325. }
  326. len = trace->dt->printPoint(s, freeLen, t, v);
  327. s += len;
  328. freeLen -= len;
  329. /* if output buffer is full, next data will be sent later */
  330. if (freeLen < 50) {
  331. ODF_arg->lastSegment = false;
  332. break;
  333. }
  334. }
  335. }
  336. /* print last point */
  337. if (!readPtrOverflowed && ODF_arg->lastSegment) {
  338. v = trace->valuePrev;
  339. t = trace->lastTimeStamp;
  340. len = trace->dt->printPointEnd(s, freeLen, t, v);
  341. s += len;
  342. freeLen -= len;
  343. }
  344. } while(readPtrOverflowed);
  345. ODF_arg->dataLength -= freeLen;
  346. }
  347. }
  348. break;
  349. }
  350. return ret;
  351. }
  352. /******************************************************************************/
  353. void CO_trace_init(
  354. CO_trace_t *trace,
  355. CO_SDO_t *SDO,
  356. uint8_t enabled,
  357. uint32_t *timeBuffer,
  358. int32_t *valueBuffer,
  359. uint32_t bufferSize,
  360. uint32_t *map,
  361. uint8_t *format,
  362. uint8_t *trigger,
  363. int32_t *threshold,
  364. int32_t *value,
  365. int32_t *minValue,
  366. int32_t *maxValue,
  367. uint32_t *triggerTime,
  368. uint16_t idx_OD_traceConfig,
  369. uint16_t idx_OD_trace)
  370. {
  371. trace->SDO = SDO;
  372. trace->enabled = (enabled != 0) ? true : false;
  373. trace->timeBuffer = timeBuffer;
  374. trace->valueBuffer = valueBuffer;
  375. trace->bufferSize = bufferSize;
  376. trace->writePtr = 0;
  377. trace->readPtr = 0;
  378. trace->lastTimeStamp = 0;
  379. trace->map = map;
  380. trace->format = format;
  381. trace->trigger = trigger;
  382. trace->threshold = threshold;
  383. trace->value = value;
  384. trace->minValue = minValue;
  385. trace->maxValue = maxValue;
  386. trace->triggerTime = triggerTime;
  387. *trace->value = 0;
  388. *trace->minValue = 0;
  389. *trace->maxValue = 0;
  390. *trace->triggerTime = 0;
  391. trace->valuePrev = 0;
  392. /* set trace->OD_variable and trace->dt, based on 'map' and 'format' */
  393. findVariable(trace);
  394. if (timeBuffer == NULL || valueBuffer == NULL) {
  395. trace->bufferSize = 0;
  396. }
  397. if ( trace->bufferSize == 0 || trace->OD_variable == NULL) {
  398. trace->enabled = false;
  399. }
  400. CO_OD_configure(SDO, idx_OD_traceConfig, CO_ODF_traceConfig, (void*)trace, 0, 0);
  401. CO_OD_configure(SDO, idx_OD_trace, CO_ODF_trace, (void*)trace, 0, 0);
  402. }
  403. /******************************************************************************/
  404. void CO_trace_process(CO_trace_t *trace, uint32_t timestamp) {
  405. if (trace->enabled) {
  406. int32_t val = trace->dt->pGetValue(trace->OD_variable);
  407. if (val != trace->valuePrev) {
  408. /* Verify, if value passed threshold */
  409. if ((*trace->trigger & 1) != 0 && trace->valuePrev < *trace->threshold && val >= *trace->threshold) {
  410. *trace->triggerTime = timestamp;
  411. }
  412. if ((*trace->trigger & 2) != 0 && trace->valuePrev < *trace->threshold && val >= *trace->threshold) {
  413. *trace->triggerTime = timestamp;
  414. }
  415. /* Write value and verify min/max */
  416. if (trace->value != trace->OD_variable) {
  417. *trace->value = val;
  418. }
  419. trace->valuePrev = val;
  420. if (*trace->minValue > val) {
  421. *trace->minValue = val;
  422. }
  423. if (*trace->maxValue < val) {
  424. *trace->maxValue = val;
  425. }
  426. /* write buffers and update pointers */
  427. trace->timeBuffer[trace->writePtr] = timestamp;
  428. trace->valueBuffer[trace->writePtr] = val;
  429. if (++trace->writePtr == trace->bufferSize) {
  430. trace->writePtr = 0;
  431. }
  432. if (trace->writePtr == trace->readPtr) {
  433. if (++trace->readPtr == trace->bufferSize) {
  434. trace->readPtr = 0;
  435. }
  436. }
  437. }
  438. else {
  439. /* if buffer is empty, make first record */
  440. if (trace->writePtr == trace->readPtr) {
  441. /* write buffers and update pointers */
  442. trace->timeBuffer[trace->writePtr] = timestamp;
  443. trace->valueBuffer[trace->writePtr] = val;
  444. if (++trace->writePtr == trace->bufferSize) {
  445. trace->writePtr = 0;
  446. }
  447. }
  448. }
  449. trace->lastTimeStamp = timestamp;
  450. }
  451. }
  452. #endif /* (CO_CONFIG_TRACE) & CO_CONFIG_TRACE_ENABLE */