diff --git a/python/pyrogue/_Root.py b/python/pyrogue/_Root.py index f13b82218..45a1aa8b7 100644 --- a/python/pyrogue/_Root.py +++ b/python/pyrogue/_Root.py @@ -65,11 +65,12 @@ def decrement(self): self._check() def _check(self): - if len(self._list) != 0 and (self._count == 0 or (self._period != 0 and (time.time() - self._last) > self._period)): - #print(f"Update fired {time.time()}") - self._last = time.time() - self._q.put(self._list) - self._list = {} + if self._count == 0 or (self._period != 0 and (time.time() - self._last) > self._period): + if len(self._list) != 0: + #print(f"Update fired {time.time()}") + self._last = time.time() + self._q.put(self._list) + self._list = {} def update(self,var): """ @@ -482,19 +483,19 @@ def updateGroup(self, period=0): tid = threading.get_ident() # At with call - with self._updateLock: - if tid not in self._updateTrack: - self._updateTrack[tid] = UpdateTracker(self._updateQueue) - + try: self._updateTrack[tid].increment(period) + except Exception: + with self._updateLock: + self._updateTrack[tid] = UpdateTracker(self._updateQueue) + self._updateTrack[tid].increment(period) try: yield finally: # After with is done - with self._updateLock: - self._updateTrack[tid].decrement() + self._updateTrack[tid].decrement() @contextmanager def pollBlock(self): @@ -1000,10 +1001,19 @@ def _queueUpdates(self,var): """ tid = threading.get_ident() - with self._updateLock: - if tid not in self._updateTrack: - self._updateTrack[tid] = UpdateTracker(self._updateQueue) + try: self._updateTrack[tid].update(var) + except Exception: + with self._updateLock: + self._updateTrack[tid] = UpdateTracker(self._updateQueue) + self._updateTrack[tid].update(var) + + # Recursively add listeners to update list + def _recurseAddListeners(self, nvars, var): + for vl in var._listeners: + nvars[vl.path] = vl + + self._recurseAddListeners(nvars, vl) # Worker thread def _updateWorker(self): @@ -1022,10 +1032,19 @@ def _updateWorker(self): # Process list elif len(uvars) > 0: self._log.debug(F'Process update group. Length={len(uvars)}. Entry={list(uvars.keys())[0]}') + + # Copy list and add listeners + nvars = uvars.copy() for p,v in uvars.items(): + self._recurseAddListeners(nvars, v) + + # Process the new list + for p,v in nvars.items(): + + # Process updates val = v._doUpdate() - # Call listener functions, + # Call root listener functions, with self._varListenLock: for func,doneFunc,incGroups,excGroups in self._varListeners: if v.filterByGroup(incGroups, excGroups): @@ -1040,7 +1059,7 @@ def _updateWorker(self): else: pr.logException(self._log,e) - # Finalize listeners + # Finalize root listeners with self._varListenLock: for func,doneFunc,incGroups,excGroups in self._varListeners: if doneFunc is not None: diff --git a/python/pyrogue/_Variable.py b/python/pyrogue/_Variable.py index 28c4b3f11..c07dfb129 100644 --- a/python/pyrogue/_Variable.py +++ b/python/pyrogue/_Variable.py @@ -860,9 +860,6 @@ def _queueUpdate(self): """ """ self._root._queueUpdates(self) - for var in self._listeners: - var._queueUpdate() - def _doUpdate(self): """ """ val = VariableValue(self) diff --git a/src/rogue/interfaces/memory/Block.cpp b/src/rogue/interfaces/memory/Block.cpp index 2c81292ea..563d5c923 100644 --- a/src/rogue/interfaces/memory/Block.cpp +++ b/src/rogue/interfaces/memory/Block.cpp @@ -903,6 +903,7 @@ void rim::Block::setUIntPy(bp::object& value, rim::Variable* var, int32_t index) PyArrayObject* arr = reinterpret_cast(value.ptr()); npy_intp ndims = PyArray_NDIM(arr); npy_intp* dims = PyArray_SHAPE(arr); + npy_intp* strides = PyArray_STRIDES(arr); if (ndims != 1) throw(rogue::GeneralError::create("Block::setUIntPy", @@ -920,11 +921,17 @@ void rim::Block::setUIntPy(bp::object& value, rim::Variable* var, int32_t index) var->name_.c_str())); if (PyArray_TYPE(arr) == NPY_UINT64) { - uint64_t* src = reinterpret_cast(PyArray_DATA(arr)); - for (x = 0; x < dims[0]; x++) setUInt(src[x], var, index + x); + uint64_t* src = reinterpret_cast(PyArray_DATA(arr)); + npy_intp stride = strides[0] / sizeof(uint64_t); + for (x = 0; x < dims[0]; x++) { + setUInt(src[x * stride], var, index + x); + } } else if (PyArray_TYPE(arr) == NPY_UINT32) { - uint32_t* src = reinterpret_cast(PyArray_DATA(arr)); - for (x = 0; x < dims[0]; x++) setUInt(src[x], var, index + x); + uint32_t* src = reinterpret_cast(PyArray_DATA(arr)); + npy_intp stride = strides[0] / sizeof(uint32_t); + for (x = 0; x < dims[0]; x++) { + setUInt(src[x * stride], var, index + x); + } } else { throw(rogue::GeneralError::create("Block::setUIntPy", "Passed nparray is not of type (uint64 or uint32) for %s", @@ -1061,6 +1068,7 @@ void rim::Block::setIntPy(bp::object& value, rim::Variable* var, int32_t index) PyArrayObject* arr = reinterpret_cast(value.ptr()); npy_intp ndims = PyArray_NDIM(arr); npy_intp* dims = PyArray_SHAPE(arr); + npy_intp* strides = PyArray_STRIDES(arr); if (ndims != 1) throw(rogue::GeneralError::create("Block::setIntPy", @@ -1078,11 +1086,17 @@ void rim::Block::setIntPy(bp::object& value, rim::Variable* var, int32_t index) var->name_.c_str())); if (PyArray_TYPE(arr) == NPY_INT64) { - int64_t* src = reinterpret_cast(PyArray_DATA(arr)); - for (x = 0; x < dims[0]; x++) setInt(src[x], var, index + x); + int64_t* src = reinterpret_cast(PyArray_DATA(arr)); + npy_intp stride = strides[0] / sizeof(int64_t); + for (x = 0; x < dims[0]; x++) { + setInt(src[x * stride], var, index + x); + } } else if (PyArray_TYPE(arr) == NPY_INT32) { - int32_t* src = reinterpret_cast(PyArray_DATA(arr)); - for (x = 0; x < dims[0]; x++) setInt(src[x], var, index + x); + int32_t* src = reinterpret_cast(PyArray_DATA(arr)); + npy_intp stride = strides[0] / sizeof(int32_t); + for (x = 0; x < dims[0]; x++) { + setInt(src[x * stride], var, index + x); + } } else { throw(rogue::GeneralError::create("Block::setIntPy", "Passed nparray is not of type (int64 or int32) for %s", @@ -1223,6 +1237,7 @@ void rim::Block::setBoolPy(bp::object& value, rim::Variable* var, int32_t index) PyArrayObject* arr = reinterpret_cast(value.ptr()); npy_intp ndims = PyArray_NDIM(arr); npy_intp* dims = PyArray_SHAPE(arr); + npy_intp* strides = PyArray_STRIDES(arr); if (ndims != 1) throw(rogue::GeneralError::create("Block::setBoolPy", @@ -1240,8 +1255,11 @@ void rim::Block::setBoolPy(bp::object& value, rim::Variable* var, int32_t index) var->name_.c_str())); if (PyArray_TYPE(arr) == NPY_BOOL) { - bool* src = reinterpret_cast(PyArray_DATA(arr)); - for (x = 0; x < dims[0]; x++) setBool(src[x], var, index + x); + bool* src = reinterpret_cast(PyArray_DATA(arr)); + npy_intp stride = strides[0] / sizeof(bool); + for (x = 0; x < dims[0]; x++) { + setBool(src[x * stride], var, index + x); + } } else { throw(rogue::GeneralError::create("Block::setBoolPy", "Passed nparray is not of type (bool) for %s", @@ -1431,6 +1449,7 @@ void rim::Block::setFloatPy(bp::object& value, rim::Variable* var, int32_t index PyArrayObject* arr = reinterpret_cast(value.ptr()); npy_intp ndims = PyArray_NDIM(arr); npy_intp* dims = PyArray_SHAPE(arr); + npy_intp* strides = PyArray_STRIDES(arr); if (ndims != 1) throw(rogue::GeneralError::create("Block::setFloatPy", @@ -1448,8 +1467,11 @@ void rim::Block::setFloatPy(bp::object& value, rim::Variable* var, int32_t index var->name_.c_str())); if (PyArray_TYPE(arr) == NPY_FLOAT32) { - float* src = reinterpret_cast(PyArray_DATA(arr)); - for (x = 0; x < dims[0]; x++) setFloat(src[x], var, index + x); + float* src = reinterpret_cast(PyArray_DATA(arr)); + npy_intp stride = strides[0] / sizeof(float); + for (x = 0; x < dims[0]; x++) { + setFloat(src[x * stride], var, index + x); + } } else { throw(rogue::GeneralError::create("Block::setFLoatPy", "Passed nparray is not of type (float32) for %s", @@ -1576,6 +1598,7 @@ void rim::Block::setDoublePy(bp::object& value, rim::Variable* var, int32_t inde PyArrayObject* arr = reinterpret_cast(value.ptr()); npy_intp ndims = PyArray_NDIM(arr); npy_intp* dims = PyArray_SHAPE(arr); + npy_intp* strides = PyArray_STRIDES(arr); if (ndims != 1) throw(rogue::GeneralError::create("Block::setDoublePy", @@ -1593,8 +1616,11 @@ void rim::Block::setDoublePy(bp::object& value, rim::Variable* var, int32_t inde var->name_.c_str())); if (PyArray_TYPE(arr) == NPY_FLOAT64) { - double* src = reinterpret_cast(PyArray_DATA(arr)); - for (x = 0; x < dims[0]; x++) setDouble(src[x], var, index + x); + double* src = reinterpret_cast(PyArray_DATA(arr)); + npy_intp stride = strides[0] / sizeof(double); + for (x = 0; x < dims[0]; x++) { + setDouble(src[x * stride], var, index + x); + } } else { throw(rogue::GeneralError::create("Block::setFLoatPy", "Passed nparray is not of type (double) for %s", @@ -1721,6 +1747,7 @@ void rim::Block::setFixedPy(bp::object& value, rim::Variable* var, int32_t index PyArrayObject* arr = reinterpret_cast(value.ptr()); npy_intp ndims = PyArray_NDIM(arr); npy_intp* dims = PyArray_SHAPE(arr); + npy_intp* strides = PyArray_STRIDES(arr); if (ndims != 1) throw(rogue::GeneralError::create("Block::setFixedPy", @@ -1738,8 +1765,11 @@ void rim::Block::setFixedPy(bp::object& value, rim::Variable* var, int32_t index var->name_.c_str())); if (PyArray_TYPE(arr) == NPY_FLOAT64) { - double* src = reinterpret_cast(PyArray_DATA(arr)); - for (x = 0; x < dims[0]; x++) setFixed(src[x], var, index + x); + double* src = reinterpret_cast(PyArray_DATA(arr)); + npy_intp stride = strides[0] / sizeof(double); + for (x = 0; x < dims[0]; x++) { + setFixed(src[x * stride], var, index + x); + } } else { throw(rogue::GeneralError::create("Block::setFixedPy", "Passed nparray is not of type (double) for %s", diff --git a/tests/test_list_memory.py b/tests/test_list_memory.py index 8e4c80e2e..fb1016a9f 100644 --- a/tests/test_list_memory.py +++ b/tests/test_list_memory.py @@ -444,6 +444,30 @@ def test_memory(): # Test value shift _ = resA[0] >> 5 + root.ListDevice.UInt32List.set(UInt32ListA[::2]) + root.ListDevice.Int32List.set(Int32ListA[::2]) + + resA = root.ListDevice.UInt32List.get() + resB = root.ListDevice.Int32List.get() + + for i in range(16): + + if resA[i] != UInt32ListA[::2][i]: + raise AssertionError(f'Stripe Verification Failure for UInt32ListA at position {i}') + + if resB[i] != Int32ListA[::2][i]: + raise AssertionError(f'Stripe Verification Failure for Int32ListA at position {i}') + + for i in range(16, 32): + + if resA[i] != UInt32ListA[i]: + raise AssertionError(f'Stripe Verification Failure for UInt32ListA at position {i}') + + if resB[i] != Int32ListA[i]: + raise AssertionError(f'Stripe Verification Failure for Int32ListA at position {i}') + + + def run_gui(): import pyrogue.pydm diff --git a/tests/test_rate.py b/tests/test_rate.py index ffbb00a97..1b2c9bdcd 100644 --- a/tests/test_rate.py +++ b/tests/test_rate.py @@ -16,6 +16,9 @@ import time import hwcounter +#import cProfile, pstats, io +#from pstats import SortKey + #rogue.Logging.setLevel(rogue.Logging.Debug) #import logging #logger = logging.getLogger('pyrogue') @@ -105,6 +108,9 @@ def __init__(self): def test_rate(): + #pr = cProfile.Profile() + #pr.enable() + with DummyTree() as root: count = 100000 resultRate = {} @@ -178,5 +184,14 @@ def test_rate(): if passed is False: raise AssertionError('Rate check failed') + + #pr.disable() + + #s = io.StringIO() + #sortby = SortKey.CUMULATIVE + #ps = pstats.Stats(pr, stream=s).sort_stats(sortby) + #ps.print_stats() + #print(s.getvalue()) + if __name__ == "__main__": test_rate()