rogue
Loading...
Searching...
No Matches
Frame.cpp
Go to the documentation of this file.
1
18#include "rogue/Directives.h"
19
20#ifndef NO_PYTHON
21
22#define NO_IMPORT_ARRAY
23#include "rogue/numpy.h"
24#include <numpy/ndarraytypes.h>
25#include <boost/python.hpp>
26
27namespace bp = boost::python;
28
29#endif
30
32
33#include <inttypes.h>
34
35#include <cstdio>
36#include <memory>
37
38#include "rogue/GeneralError.h"
42
44
46ris::FramePtr ris::Frame::create() {
47 ris::FramePtr frame = std::make_shared<ris::Frame>();
48 return (frame);
49}
50
52ris::Frame::Frame() {
53 flags_ = 0;
54 error_ = 0;
55 size_ = 0;
56 chan_ = 0;
57 payload_ = 0;
58 sizeDirty_ = false;
59}
60
62ris::Frame::~Frame() {}
63
65ris::FrameLockPtr ris::Frame::lock() {
66 return (ris::FrameLock::create(shared_from_this()));
67}
68
70ris::Frame::BufferIterator ris::Frame::appendBuffer(ris::BufferPtr buff) {
71 uint32_t oSize = buffers_.size();
72
73 buff->setFrame(shared_from_this());
74 buffers_.push_back(buff);
75 sizeDirty_ = true;
76 return (buffers_.begin() + oSize);
77}
78
80ris::Frame::BufferIterator ris::Frame::appendFrame(ris::FramePtr frame) {
81 uint32_t oSize = buffers_.size();
82
83 for (ris::Frame::BufferIterator it = frame->beginBuffer(); it != frame->endBuffer(); ++it) {
84 (*it)->setFrame(shared_from_this());
85 buffers_.push_back(*it);
86 }
87 frame->clear();
88 sizeDirty_ = true;
89 return (buffers_.begin() + oSize);
90}
91
93ris::Frame::BufferIterator ris::Frame::beginBuffer() {
94 return (buffers_.begin());
95}
96
98ris::Frame::BufferIterator ris::Frame::endBuffer() {
99 return (buffers_.end());
100}
101
103uint32_t ris::Frame::bufferCount() {
104 return (buffers_.size());
105}
106
108void ris::Frame::clear() {
109 buffers_.clear();
110 size_ = 0;
111 payload_ = 0;
112}
113
115bool ris::Frame::isEmpty() {
116 return (buffers_.empty());
117}
118
120void ris::Frame::updateSizes() {
121 ris::Frame::BufferIterator it;
122
123 size_ = 0;
124 payload_ = 0;
125
126 for (it = buffers_.begin(); it != buffers_.end(); ++it) {
127 payload_ += (*it)->getPayload();
128 size_ += (*it)->getSize();
129 }
130 sizeDirty_ = false;
131}
132
134void ris::Frame::setSizeDirty() {
135 sizeDirty_ = true;
136}
137
138/*
139 * Get size of buffers that can hold
140 * payload data. This function
141 * returns the full buffer size minus
142 * the head and tail reservation.
143 */
144uint32_t ris::Frame::getSize() {
145 if (sizeDirty_) updateSizes();
146 return (size_);
147}
148
149/*
150 * Get available size for payload
151 * This is the space remaining for payload
152 * minus the space reserved for the tail
153 */
154uint32_t ris::Frame::getAvailable() {
155 if (sizeDirty_) updateSizes();
156 return (size_ - payload_);
157}
158
159/*
160 * Get real payload size without header
161 * This is the count of real data in the
162 * packet, minus the portion reserved for
163 * the head.
164 */
165uint32_t ris::Frame::getPayload() {
166 if (sizeDirty_) updateSizes();
167 return (payload_);
168}
169
170/*
171 * Set payload size (not including header)
172 * If passed size is less then current,
173 * the frame payload size will be decreased.
174 */
175void ris::Frame::setPayload(uint32_t pSize) {
176 ris::Frame::BufferIterator it;
177
178 uint32_t lSize;
179 uint32_t loc;
180
181 lSize = pSize;
182 size_ = 0;
183 for (it = buffers_.begin(); it != buffers_.end(); ++it) {
184 loc = (*it)->getSize();
185 size_ += loc;
186
187 // Beyond the fill point, empty buffer
188 if (lSize == 0) {
189 (*it)->setPayloadEmpty();
190
191 // Size exists in current buffer
192 } else if (lSize <= loc) {
193 (*it)->setPayload(lSize);
194 lSize = 0;
195
196 // Size is beyond current buffer
197 } else {
198 lSize -= loc;
199 (*it)->setPayloadFull();
200 }
201 }
202
203 if (lSize != 0)
204 throw(rogue::GeneralError::create("Frame::setPayload",
205 "Attempt to set payload to size %" PRIu32 " in frame with size %" PRIu32,
206 pSize,
207 size_));
208
209 // Refresh
210 payload_ = pSize;
211 sizeDirty_ = false;
212}
213
214/*
215 * Set the min payload size (not including header)
216 * If the current payload size is greater, the
217 * payload size will be unchanged.
218 */
219void ris::Frame::minPayload(uint32_t size) {
220 if (size > getPayload()) setPayload(size);
221}
222
224void ris::Frame::adjustPayload(int32_t value) {
225 uint32_t size = getPayload();
226
227 if (value < 0 && static_cast<uint32_t>(abs(value)) > size)
228 throw(rogue::GeneralError::create("Frame::adjustPayload",
229 "Attempt to reduce payload by %" PRIi32 " in frame with size %" PRIu32,
230 value,
231 size));
232
233 setPayload(size + value);
234}
235
237void ris::Frame::setPayloadFull() {
238 ris::Frame::BufferIterator it;
239
240 size_ = 0;
241 payload_ = 0;
242
243 for (it = buffers_.begin(); it != buffers_.end(); ++it) {
244 (*it)->setPayloadFull();
245
246 payload_ += (*it)->getPayload();
247 size_ += (*it)->getSize();
248 }
249 sizeDirty_ = false;
250}
251
253void ris::Frame::setPayloadEmpty() {
254 ris::Frame::BufferIterator it;
255
256 size_ = 0;
257 payload_ = 0;
258
259 for (it = buffers_.begin(); it != buffers_.end(); ++it) {
260 (*it)->setPayloadEmpty();
261
262 payload_ += (*it)->getPayload();
263 size_ += (*it)->getSize();
264 }
265 sizeDirty_ = false;
266}
267
269uint16_t ris::Frame::getFlags() {
270 return (flags_);
271}
272
274void ris::Frame::setFlags(uint16_t flags) {
275 flags_ = flags;
276}
277
278// Get first user field portion of flags (SSI/axi-stream)
279uint8_t ris::Frame::getFirstUser() {
280 uint8_t ret = flags_ & 0xFF;
281 return ret;
282}
283
284// Set first user field portion of flags (SSI/axi-stream)
285void ris::Frame::setFirstUser(uint8_t fuser) {
286 flags_ |= fuser;
287}
288
289// Get last user field portion of flags (SSI/axi-stream)
290uint8_t ris::Frame::getLastUser() {
291 uint8_t ret = (flags_ >> 8) & 0xFF;
292 return ret;
293}
294
295// Set last user field portion of flags (SSI/axi-stream)
296void ris::Frame::setLastUser(uint8_t fuser) {
297 uint16_t tmp = fuser;
298 flags_ |= (tmp << 8) & 0xFF00;
299}
300
302uint8_t ris::Frame::getError() {
303 return (error_);
304}
305
307void ris::Frame::setError(uint8_t error) {
308 error_ = error;
309}
310
312uint8_t ris::Frame::getChannel() {
313 return chan_;
314}
315
317void ris::Frame::setChannel(uint8_t channel) {
318 chan_ = channel;
319}
320
322ris::FrameIterator ris::Frame::begin() {
323 return ris::FrameIterator(shared_from_this(), false, false);
324}
325
327ris::FrameIterator ris::Frame::end() {
328 return ris::FrameIterator(shared_from_this(), false, true);
329}
330
332ris::FrameIterator ris::Frame::beginRead() {
333 return ris::FrameIterator(shared_from_this(), false, false);
334}
335
337ris::FrameIterator ris::Frame::endRead() {
338 return ris::FrameIterator(shared_from_this(), false, true);
339}
340
342ris::FrameIterator ris::Frame::beginWrite() {
343 return ris::FrameIterator(shared_from_this(), true, false);
344}
345
347ris::FrameIterator ris::Frame::endWrite() {
348 return ris::FrameIterator(shared_from_this(), true, true);
349}
350
351#ifndef NO_PYTHON
352
354void ris::Frame::readPy(boost::python::object p, uint32_t offset) {
355 Py_buffer pyBuf;
356
357 if (PyObject_GetBuffer(p.ptr(), &pyBuf, PyBUF_CONTIG) < 0)
358 throw(rogue::GeneralError("Frame::readPy", "Python Buffer Error In Frame"));
359
360 uint32_t size = getPayload();
361 uint32_t count = pyBuf.len;
362
363 if ((offset + count) > size) {
364 PyBuffer_Release(&pyBuf);
365 throw(rogue::GeneralError::create("Frame::readPy",
366 "Attempt to read %" PRIu32 " bytes from frame at offset %" PRIu32
367 " with size %" PRIu32,
368 count,
369 offset,
370 size));
371 }
372
373 ris::FrameIterator beg = this->begin() + offset;
374 ris::fromFrame(beg, count, reinterpret_cast<uint8_t*>(pyBuf.buf));
375 PyBuffer_Release(&pyBuf);
376}
377
379bp::object ris::Frame::getBytearrayPy(uint32_t offset, uint32_t count) {
380 // Get the size of the frame
381 uint32_t size = getPayload();
382
383 if (count == 0) {
384 count = size - offset;
385 }
386
387 // Create a Python bytearray to hold the data
388 bp::object byteArray(bp::handle<>(PyByteArray_FromStringAndSize(nullptr, count)));
389
390 // readPy will check bounds
391 this->readPy(byteArray, offset);
392
393 return byteArray;
394}
395
397bp::object ris::Frame::getMemoryviewPy() {
398 // Get the size of the frame
399 uint32_t size = getPayload();
400
401 // Create a Python bytearray to hold the data
402 bp::object byteArray(bp::handle<>(PyByteArray_FromStringAndSize(nullptr, size)));
403
404 this->readPy(byteArray, 0);
405
406 // Create a memoryview from the bytearray
407 PyObject* memoryView = PyMemoryView_FromObject(byteArray.ptr());
408 if (!memoryView) {
409 throw(rogue::GeneralError::create("Frame::getMemoryviewPy", "Failed to create memoryview."));
410 }
411
412 // Return the memoryview as a Python object
413 return bp::object(bp::handle<>(memoryView));
414}
415
417void ris::Frame::writePy(boost::python::object p, uint32_t offset) {
418 Py_buffer pyBuf;
419
420 if (PyObject_GetBuffer(p.ptr(), &pyBuf, PyBUF_SIMPLE) < 0)
421 throw(rogue::GeneralError("Frame::writePy", "Python Buffer Error In Frame"));
422
423 uint32_t size = getSize();
424 uint32_t count = pyBuf.len;
425
426 if ((offset + count) > size) {
427 PyBuffer_Release(&pyBuf);
428 throw(rogue::GeneralError::create("Frame::writePy",
429 "Attempt to write %" PRIu32 " bytes to frame at offset %" PRIu32
430 " with size %" PRIu32,
431 count,
432 offset,
433 size));
434 }
435
436 minPayload(offset + count);
437 ris::FrameIterator beg = this->begin() + offset;
438 ris::toFrame(beg, count, reinterpret_cast<uint8_t*>(pyBuf.buf));
439 PyBuffer_Release(&pyBuf);
440}
441
443
444#include <numpy/arrayobject.h> // make sure this is included
445
446boost::python::object ris::Frame::getNumpy(uint32_t offset, uint32_t count) {
447 // bytes available in frame payload
448 const npy_intp size_bytes = getPayload();
449
450 // default: all remaining bytes
451 if (count == 0) {
452 if (offset > size_bytes) {
454 "Frame::getNumpy",
455 "Offset %" PRIu32 " is past end of frame (size %" PRIuPTR ")",
456 offset, static_cast<uintptr_t>(size_bytes)));
457 }
458 count = static_cast<uint32_t>(size_bytes - offset); // count in BYTES by API contract
459 }
460
461 // bounds check in BYTES
462 if ((static_cast<npy_intp>(offset) + static_cast<npy_intp>(count)) > size_bytes) {
464 "Frame::getNumpy",
465 "Attempt to read %" PRIu32 " bytes from frame at offset %" PRIu32
466 " with size %" PRIuPTR,
467 count, offset, static_cast<uintptr_t>(size_bytes)));
468 }
469
470 // allocate a 1-D np.uint8 array with 'count' elements (bytes)
471 npy_intp dims[1] = { static_cast<npy_intp>(count) };
472 PyObject* obj = PyArray_SimpleNew(/*nd*/1, dims, NPY_UINT8);
473 if (!obj) {
474 throw(rogue::GeneralError::create("Frame::getNumpy",
475 "Failed to allocate NumPy uint8 array."));
476 }
477
478 // fill it from the frame
479 auto* arr = reinterpret_cast<PyArrayObject*>(obj);
480 auto* dst = reinterpret_cast<uint8_t*>(PyArray_DATA(arr));
481
482 ris::FrameIterator beg = this->begin() + offset;
483 ris::fromFrame(beg, count, dst);
484
485 // return as boost::python object (steals ownership of obj)
486 return boost::python::object(boost::python::handle<>(obj));
487}
488
489
490
492void ris::Frame::putNumpy(boost::python::object p, uint32_t offset) {
493 // Retrieve pointer to PyObject
494 PyObject* obj = p.ptr();
495
496 // Check that this is a PyArrayObject
497 if (!PyArray_Check(obj)) {
498 throw(rogue::GeneralError("Frame::putNumpy", "Object is not a numpy array"));
499 }
500
501 // Cast to an array object and check that the numpy array
502 // data buffer is write-able and contiguous
503 // The write routine can only deal with contiguous buffers.
504 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(obj);
505 int flags = PyArray_FLAGS(arr);
506 bool ctg = flags & (NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS);
507 if (!ctg) {
508 arr = PyArray_GETCONTIGUOUS(arr);
509 }
510
511 // Get the number of bytes in both the source and destination buffers
512 uint32_t size = getSize();
513 uint32_t count = PyArray_NBYTES(arr);
514 uint32_t end = offset + count;
515
516 // Check this does not request data past the EOF
517 if (end > size) {
518 throw(rogue::GeneralError::create("Frame::putNumpy",
519 "Attempt to write %" PRIu32 " bytes to frame at offset %" PRIu32
520 " with size %" PRIu32,
521 count,
522 offset,
523 size));
524 }
525
526 uint8_t* src = reinterpret_cast<uint8_t*>(PyArray_DATA(arr));
527
528 minPayload(end);
529
530 // Write the numpy data to the array
531 ris::FrameIterator beg = this->begin() + offset;
532 ris::toFrame(beg, count, src);
533
534 // If were forced to make a temporary copy, release it
535 if (!ctg) {
536 Py_XDECREF(arr);
537 }
538
539 return;
540}
541
542#endif
543
544void ris::Frame::setup_python() {
545#ifndef NO_PYTHON
546
547 // Create a NumPy dtype object from the NPY_UINT8 constant
548 PyObject* dtype_uint8 = reinterpret_cast<PyObject*>(PyArray_DescrFromType(NPY_UINT8));
549 if (!dtype_uint8) {
550 throw(
551 rogue::GeneralError::create("Frame::setup_python", "Failed to create default dtype object for NPY_UINT8."));
552 }
553
554 bp::class_<ris::Frame, ris::FramePtr, boost::noncopyable>("Frame", bp::no_init)
555 .def("lock", &ris::Frame::lock)
556 .def("getSize", &ris::Frame::getSize)
557 .def("getAvailable", &ris::Frame::getAvailable)
558 .def("getPayload", &ris::Frame::getPayload)
559 .def("read", &ris::Frame::readPy, (bp::arg("offset") = 0))
560 .def("getBa", &ris::Frame::getBytearrayPy, (bp::arg("offset") = 0, bp::arg("count") = 0))
561 .def("getMemoryview", &ris::Frame::getMemoryviewPy)
562 .def("write", &ris::Frame::writePy, (bp::arg("offset") = 0))
563 .def("setError", &ris::Frame::setError)
564 .def("getError", &ris::Frame::getError)
565 .def("setFlags", &ris::Frame::setFlags)
566 .def("getFlags", &ris::Frame::getFlags)
567 .def("setFirstUser", &ris::Frame::setFirstUser)
568 .def("getFirstUser", &ris::Frame::getFirstUser)
569 .def("setLastUser", &ris::Frame::setLastUser)
570 .def("getLastUser", &ris::Frame::getLastUser)
571 .def("setChannel", &ris::Frame::setChannel)
572 .def("getChannel", &ris::Frame::getChannel)
573 .def("getNumpy",
574 &ris::Frame::getNumpy,
575 (bp::arg("offset") = 0,
576 bp::arg("count") = 0))
577 .def("putNumpy",
578 &ris::Frame::putNumpy,
579 (bp::arg("offset") = 0))
580 .def("_debug", &ris::Frame::debug);
581#endif
582}
583
584void ris::Frame::debug() {
585 ris::Frame::BufferIterator it;
586 uint32_t idx = 0;
587
588 printf("Frame Info. BufferCount: %" PRIu32 ", Size: %" PRIu32 ", Available: %" PRIu32 ", Payload: %" PRIu32
589 ", Channel: %" PRIu8 ", Error: 0x%" PRIx8 ", Flags: 0x%" PRIx16 "\n",
590 bufferCount(),
591 getSize(),
592 getAvailable(),
593 getPayload(),
594 getChannel(),
595 getError(),
596 getFlags());
597
598 for (it = buffers_.begin(); it != buffers_.end(); ++it) {
599 (*it)->debug(idx);
600 idx++;
601 }
602}
Generic Rogue exception type.
static GeneralError create(std::string src, const char *fmt,...)
Creates a formatted error instance.
Random-access byte iterator across a Frame payload.
static void fromFrame(rogue::interfaces::stream::FrameIterator &iter, uint32_t size, void *dst)
Copies bytes from a frame iterator to a destination pointer.
std::shared_ptr< rogue::interfaces::stream::Buffer > BufferPtr
Shared pointer alias for Buffer.
Definition Buffer.h:270
std::shared_ptr< rogue::interfaces::stream::Frame > FramePtr
Shared pointer alias for Frame.
Definition Frame.h:549
static void toFrame(rogue::interfaces::stream::FrameIterator &iter, uint32_t size, void *src)
Copies bytes from a source pointer into a frame iterator.
std::shared_ptr< rogue::interfaces::stream::FrameLock > FrameLockPtr
Shared pointer alias for FrameLock.
Definition FrameLock.h:110