diff --git a/doc/exceptionHandlingDesign.dox b/doc/exceptionHandlingDesign.dox index d349475dd79f98aae86eb85e9e510a5d5f729cd9..21b93e479952f7e028361658884b21c4a3e0ac16 100644 --- a/doc/exceptionHandlingDesign.dox +++ b/doc/exceptionHandlingDesign.dox @@ -1,82 +1,96 @@ // put the namespace around the doxygen block so we don't have to give it all the time in the code to get links namespace ChimeraTK { /** -\page exceptionHandlingDesign Technical specification: Exception Handling Design +\page spec_execptionHandling Technical specification: Exception handling for device runtime errors <b>DRAFT VERSION, WRITE-UP IN PROGRESS!</b> -\section gen_idea General Idea - -Exceptions must be handled by ApplicationCore in a way that the application developer does not have to care much about it. - -In case of a runtime_error exception the framework must catch the exception and report it to the DeviceModule. The DeviceModule handles this exception and periodically tries to open the device. In case of several devices only the faulty device is blocked. Even if a device is faulty it should not block the server from starting. - -If an input variable is in the error state, it sets the DataValidity flag for its DataFaultCounter (see \link spec_dataValidityPropagation \endlink) to faulty and the flag is propagated appropriately. After the exception is cleared and operation returns without a data fault flag, set DataValidity flag to ok. Furthermore, the device must be reinitialised automatically and also recover the values of process variables as the device might have rebooted and the variables have been re-set. - -<b>1. Genesis</b> -- a (removed) -- b. An initialisation handler can be added to the DeviceModule in the user code. Initialisation handlers are callback function which will be executed when a device is opened for the first time and after a device recovers from an exception, before any process variables are written. -- c. Initial values must be correctly propagated after a device is opened. See \link spec_initialValuePropagation \endlink. Especially, no read function (even readNonBlocking/readLatest) must return before an initial value has been received. -- d. (removed) -- e. An ExceptionHandlingDecorator is placed around all NDRegisterAccessors which connect a device to a ApplicationModule or fanout. (*) -- f. (removed) -- g. By default a recovery accessor is added for each device register when it is obtained. These recovery accessors are used to correctly set the values of variables when the device is opened for the first time and after a device is recovered from an exception. (*) -- h. An ExceptionHandlingDecorator for an input knows its DataFaultCounter, which lives in the ApplicationModule or fanout that reads the input. Like this it can propagate the - DataValidity flag. Outputs do not send DataValidity faulty in case of exceptions (see \link spec_dataValidityPropagation \endlink). -- i. Write should not block in case of an exception for the outputs of ThreadedFanOut / TriggerFanOut. (*) -- j. Exception handling and invalid flag propagation has to be implemented such that it is transparent to a module whether it is directly connected to a device, or whether a fanout or another application module is in between. -- k. The server must start even if some devices are in error state. The devices which are working and all modules that do not talk to the broken device must work. -- l. After a device has been re-opened, all values that had once been written must be re-written. - -<b>2. The Flow</b> - -- 2.1. The application always starts with all devices as closed. The initial value for deviceError.status is set to 1 and the initial value for deviceError.message is set to an error that the device has not been opened yet (the message will be overwritten with the real error message if the first attempt to open fails, see 2.3.1). The DeviceModule takes care that ExceptionHandlingDecorators do not perform any read or write operations, but block. This must happen before running any prepare() of an ApplicationModule, where the first write calls to ExceptionHandlingDecorators are done. - -- 2.2 In ApplicationModule::prepare() some initial values (and constants) are written. As the ExceptionHandlingDecorator must not perform the actual write at this point, it will put the value into the dataRecoveryAccesssor and report an exception to the DeviceModule. - -- Between 2.2 and 2.3 the application goes into multi-threaded mode. Although ApplicationModule and fanout threads start after the device module threads, the application is now asynchronous and read or write operations can already take place in the main loops, even if the device is not ready yet (it might actually be broken). All read and write operations are blocked buy the ExceptionHandlingDecorators at this point. - -- 2.3 The device module thread starts. - - 2.3.1 The DeviceModule tries to open the device until it succeeds. (*) - - 2.3.1.1 If the very first attempt to open the device since the application start fails, the error message of the exception is used to overwrite the content of deviceError.message. Otherwise error messages of exceptions thrown by Device::open() are not visible. - - 2.3.2 Device is initialised by iterating initialisationHandlers list. If there is an exception, update deviceError.message with the error message and go back to 2.3.1. (*) - - 2.3.3 The list of reported exceptions is cleared. (*) - - 2.3.4 All valid (*) recovery accessors are written. If there is an exception, update deviceError.message with the error message and go back to 2.3.1. (*) - - 2.3.5 deviceError.status is set to 0 and deviceError.message is set to an empty string. - - 2.3.6 DeviceModule allows that ExceptionHandlingDecorators execute reads and writes. - - 2.3.7 All blocked read and write operations (from 2.5.3) are notified.(*) - - 2.3.8 The DeviceModuleThread waits for the next reported exception. - -- 2.4 Device and Application are running normally - - 2.4.1 All blocked ExceptionHandlingDecorators continue (*) - - 2.4.1.1 write just continues (recovery accessor has done the write) - - 2.4.1.2 read/readNonBlocking/readLatest - - 2.4.1.2.1 tells the DataFaultCounter that the device error has gone - - 2.4.1.2.2 (re-)tries to get the value. In case of an exception go to 2.5 - - 2.4.2 In the ExceptionHandlingDecorator, all write calls always fill the value into the recovery accessors before trying to execute the real write. Like this, the recovery accessor always has the last value that should have been written to the device. All recovery accessors become valid over time (see comment for 2.3.4). - - 2.4.2.1 If a write is not executed because the device is already faulty (from 2.2 or 2.6.1), the recovery accessor has to take care of this. In this case we always have to send another exception notification to the DeviceModule to make sure that the new recovery value is not missed (avoid race condition). (*) - - -- 2.5. When a read / write operation on the device (1.e) causes a runtime_error exception, the exception is caught in the ExceptionHandlingDecorator - - 2.5.1. If it is a read operation the DataFaultCounter is informed that there was a device error. (*) - - 2.5.2. The error is reported to the DeviceModule - - 2.5.3. Action depending on the calling operation : - - write : blocks until the device is recovered. - - read : If the accessor has already seen its initial value, the first "blocking" read call returns immediately (remember DataValidity is set to faulty). The ExceptionHandlingDecorator remembers that it is in an exception state. The calling module thread will continue and propagate the data invalid flag. The second call will finally block. If there has not been an initial value yet, even the first call will block until it is available. +\section spec_execptionHandling_intro Introduction + +Exceptions are handled by ApplicationCore in a way that the application developer does not need to care much about it. + +ChimeraTK::runtime_error exceptions are caught by the framework and are reported to the DeviceModule. The DeviceModule handles this exception and periodically tries to open the device. Communication with the faulty device is blocked or delayed until the device is functional again. In case of several devices only the faulty device is blocked. Faulty devices do not prevent the application from starting, only the parts of the application that depend on the fault device are waiting for the device to come up. + +Input variables of ApplicationModules which cannot be read due to a faulty device will set and propagate the DataValidity::faulty flag (see also \link spec_dataValidityPropagation \endlink). + +When the device is functional, it be (re)initialised by using application-defined initialisation handlers and also recover the last known values of its process variables. + +\section spec_execptionHandling_behaviour 1. Behavioural description + +- 1.1 All ChimeraTK::runtime_error exceptions thrown by device register accessors are handled by the framework and are never exposed to user code in ApplicationModules. +- 1.2 After an exception has been received, read operations will propagate the DataValidity::faulty. Blocking read operations will block after the flag has been read (i.e. on the second read of the same accessor), while non-blocking read operations will never block. Write operations will block always. [TBD: is this really a good idea? <b>COMMENT</b>: The order of write operations is still not guaranteed through the recovery accessors (which maybe should be changed), and blocking writes has some severe drawbacks. Not only in fan outs but also in normal ApplicationModules blocking writes will prevent propagation of DataValidity flags! Blocking writes might help if a sequence of values is written to the same register - this is not handled by the recovery accessor. But if a handshake register is read back in between the writes, the situation can already be handled properly (check DataValidity flag, restart sequence after recovery). Maybe blocking writes create more probelms then they solve!? On the other hand, how does the application then know that a write() has no effect yet? E.g. a PI controller might wind-up if actuator and sensor are on different devices and the actuator fails. Then again, how is this different a failing actuator hardware without breaking the communication? Some form of a status readback of the actuator again cures the situation. I think I am in favour of "fire-and-forget" writes.]. + - 1.2.1 Write should not block in case of an exception for the outputs of ThreadedFanOut / TriggerFanOut. + - 1.2.2 According to \link spec_initialValuePropagation \endlink, writes in ApplicationModules do not block before the first successful read in the main loop. +- 1.3 The framework will try to resolve the exception state by periodically re-opening the faulty device. +- 1.4 After successfully re-opening the device, a recovery procedure is executed before allowing any read/write operations from the AppliactionModules and FanOuts again. This recovery procedure involves: + - 1.4.1 the execution of initialisation handlers, and + - 1.4.2 restoring all registers that have been written since the start of the application with their latest values. The register values are restored in the same order they were written. [<b>NEW REQUIREMENT!</b>] (*) +- 1.5 An initialisation handler can be added to the DeviceModule in the user code. Initialisation handlers are callback function which will be executed when a device is opened for the first time and after a device recovers from an exception, before any process variables are written. See DeviceModule::addInitialisationHandler(). +- 1.6 The behaviour at application start (when all devices are still closed at first) is similar to the case of a later received exception. The only differences are mentioned in 1.2.2 and 1.8. +- 1.7 Even if some devices are initially in a persisting error state, the part of the application which does not interact with the faulty devices will start and work normally. +- 1.8 Initial values are correctly propagated after a device is opened. See \link spec_initialValuePropagation \endlink. Especially, no read function (even readNonBlocking/readLatest) will return before an initial value has been received. +- 1.9 Exception handling and DataValidity flag propagation is implemented such that it is transparent to a module whether it is directly connected to a device, or whether a fanout or another application module is in between. +- 1.10 ChimeraTK::logic_error exceptions are left unhandled and will terminate the application. These errors may only occur in the initialisation phase (up to the point where all devices are opened and initialised) and point to a severe configuration error which is not recoverable. (*) + +\subsection spec_execptionHandling_behaviour_comments (*) Comments + +- 1.2.1 If writes in ThreadedFanOut/TriggerFanOut would block, the other receivers would no longer receive updates. The exact behaviour would not even be well-defined, since the order of writes in the fanouts is random. + +- 1.4.2 It may be important to guarantee the order of writes. Please note that the VersionNumber is insufficient as a sorting criteria, since many writes may have been done with the same VersionNumber (in an ApplicationModule, the VersionNumber used for the writes is determined by the largest VersionNumber of the inputs). + +- 1.8 DataValidity::faulty is set at first by default, so there is no need to propagate this flag initially. To prevent race conditions and undefined behaviour, it even needs to be made sure that the flag is not propagated unnecessarily. See also \link spec_initialValuePropagation \endlink. + +- 1.10 In future, maybe logic_errors are also handled, so configuration errors can nicely be presented in the control system. This may be important especially since logic_errors may depend on the configuration of external components (devices). If e.g. a device is changed (e.g. device is another control system application which has been modified), logic_errors may be thrown in the recovery phase, despite the device had been successfully initialsed previously. + +\section spec_execptionHandling_high_level_implmentation 2. High-level description of the implementation + +- 2.1. A so-called ExceptionHandlingDecorator is placed around all device register accessors. + - 2.1.1 A second, undecorated copy of each device register accessor is used as a so-called recoveryAccessor by the ExceptionHandlingDecorator and the DeviceModule. These recoveryAccessor are used to correctly set the values of registers when the device is opened for the first time and after a device is recovered from an exception. (*) + - 2.1.2 An ExceptionHandlingDecorator for an input knows its DataFaultCounter, which lives in the ApplicationModule or fanout that reads the input. Like this it can propagate the DataValidity flag. Outputs do not send DataValidity faulty in case of exceptions (see \link spec_dataValidityPropagation \endlink). [<b>COMMENT</b>: I think this is not necessary. The ExceptionHandlingDecorator simply has to return the right data validity flag, which will be propagated in the same way as in \link spec_dataValidityPropagation \endlink. No special treatment required for exceptions!] + +- 2.2. DeviceModule: + - 2.2.1 The application always starts with all devices as closed. For each device, the initial value for Devices/<alias>/status is set to 1 and the initial value for Devices/<alias>/message is set to an error that the device has not been opened yet (the message will be overwritten with the real error message if the first attempt to open fails, see 2.2.3.1). + - 2.2.2 The DeviceModule takes care that ExceptionHandlingDecorators initally do not perform any read or write operations, but block. This happens before running any prepare() of an ApplicationModule, where the first write calls to ExceptionHandlingDecorators are done. + - 2.2.3 In the DeviceModule thread, the following procedure is executed (in a loop until termination): + - 2.2.3.1 The DeviceModule tries to open the device until it succeeds. (*) + - 2.2.3.1.1 If the very first attempt to open the device since the application start fails, the error message of the exception is used to overwrite the content of Devices/<alias>/message. Otherwise error messages of exceptions thrown by Device::open() are not visible. + - 2.2.3.2 Device is initialised by iterating initialisationHandlers list. If there is an exception, update Devices/<alias>/message with the error message and go back to 2.2.3.1. (*) + - 2.2.3.3 The list of reported exceptions is cleared. (*) + - 2.2.3.4 All valid (*) recovery accessors are written in the same order they were originally written [<b>NEW REQUIREMENT! see 1.4.2</b>]. If there is an exception, update Devices/<alias>/message with the error message and go back to 2.2.3.1. (*) + - 2.2.3.5 Devices/<alias>/status is set to 0 and Devices/<alias>/message is set to an empty string. + - 2.2.3.6 DeviceModule allows that ExceptionHandlingDecorators execute reads and writes. + - 2.2.3.7 All blocked read and write operations (from 2.5.3) are notified.(*) + - 2.2.3.8 The DeviceModuleThread waits for the next reported exception. + - 2.2.3.9 An exception is received. + - 2.2.3.10 Devices/<alias>/status is set to 1 and Devices/<alias>/message is set to the first received exception message. + - 2.2.3.11 From this point on, all ExceptionHandlingDecorators for this device must block new read and write operations from starting (see also 2.2.2 and 2.2.3.6). + - 2.2.3.11 The device module waits until all running read and write operations of ExceptionHandlingDecorators have ended. (*) + - 2.2.3.12 The thread goes back to 2.2.3.1 and tries to re-open the device. + +- 2.3. ApplicationModule/FanOuts with ExceptionHandlingDecorators: + - 2.3.3 As per 1.6, ExceptionHandlingDecorators will initially block all read operations until 2.2.3.6. Write operations are not blocking, but the written values are recorded in the recoveryAccessor and report an exception to the DeviceModule (*). + - 2.3.4 When notified by the DeviceModule (cf. 2.2.3.7), all blocked ExceptionHandlingDecorators will unblock: + - 2.3.4.1 write just continues (recovery accessor has done the write) + - 2.3.4.2 read/readNonBlocking/readLatest + - 2.3.4.2.1 tells the DataFaultCounter that the device error has gone [<b>COMMENT</b> May not be necessary, see COMMENT in 2.1.2] + - 2.3.4.2.2 (re-)tries to get the value. Exceptions are handled in the same way as in normal read operations (see 2.3.6) + - 2.3.5 In the ExceptionHandlingDecorator, all write calls always fill the value into the recoveryAccessors before trying to execute the real write. Like this, the recoveryAccessor always has the last value that should have been written to the device. All recoveryAccessors become valid over time (see comment for 2.2.3.4). + - 2.3.5.1 If a write is not executed because the device is already faulty (from 2.2.2 or 2.2.3.11), the recoveryAccessor has to take care of this. In this case we always have to send another exception notification to the DeviceModule to make sure that the new recovery value is not missed (avoid race condition). (*) + - 2.3.6 The ExceptionHandlingDecorator will catch any runtime_error exception thrown in a read/write operation. + - 2.3.6.1. If it is a read operation the DataFaultCounter is informed that there was a device error. (*) [<b>COMMENT</b> May not be necessary, see COMMENT in 2.1.2] + - 2.3.6.2. The error is reported to the DeviceModule + - 2.3.6.3. Action depending on the calling operation: + - write: blocks until the device is recovered. [<b>See discussion in 1.2</b>] + - read: If the accessor has already seen its initial value, the first "blocking" read call returns immediately (remember DataValidity is set to faulty). The ExceptionHandlingDecorator remembers that it is in an exception state. The calling module thread will continue and propagate the data invalid flag. The second call will finally block. If there has not been an initial value yet, even the first call will block until it is available. - readNonBlocking / readLatest: will always return with data invalid flag (unless there has not been an initial value yet). - writeWithoutErrorBlocking: just returns (*) -- 2.6 The exception is received in the DeviceModule thread - - 2.6.1 deviceError.status is set to 1 and deviceError.message is set to the first received exception message. From this point on, all ExceptionHandlingDecorators for this device must block new read and write operations from starting (see also 2.2 and 2.3.6). - - 2.6.2 The device module waits until all running read and write operations have ended (*) - - 2.6.3 The thread goes back to 2.3.1 and tries to re-open the device. +\subsection spec_execptionHandling_high_level_implmentation_comments (*) Comments -<b>3. (*) Comments</b> +- 1.1 Possible future change: Output accessors can have the option not to have a recovery accessor. This is needed for instance for "trigger registers" which start an operation on the hardware. Also void registers don't have recovery accessors (once the void data type is supported). -- 1.e. In addition there can be recovery accessors for the same variables, which are not decorated. They are not directly seen by the ApplicationModule and the fanouts. -- 1.g. Output accessors can have the option not to have a recovery accessor. This is needed for instance for "trigger registers" which start an operation on the hardware. Also void registers don't have recovery accessors. -- 1.i. The specification for initial value propagation (\link spec_initialValuePropagation \endlink) also says that writes ApplicationModules don't block before the first successful read in the main loop. +- 2.3.3 This works in the same way in ApplicationModule::prepare() (where the devices will never be opened) and ApplicationModule::mainLoop() until the first device read operation of the ApplicationModule. In theory, reporting the exception to the DeviceModule would not be necessary in ApplicationModule::prepare(), but when the DeviceModule thread is running in parallel it is necessarg to avoid a race condition. See 2.3.5.1. - 2.3.1 Successful opening includes that the device reports isFunctional() as true. - 2.3.2 and 2.3.4 Exceptions for re-initialisation and recovery will be reported once, but not if it occurs again before the device has completely recovered. @@ -85,9 +99,11 @@ If an input variable is in the error state, it sets the DataValidity flag for it - 2.3.7 This is different from 2.2.6 because 2.2.6 affects accessors which want to perform a read or write, while 2.2.7 affects accessors that failed to do so and are waiting for the device to become available again. This is needed for two cases: - 1. A blocking write, where the recovery accessor has already done the job when the device if back to OK. - 2. The first blocking read if the data has not seen the initial value yet, and retrieving it caused the exception. -- 2.4.1 writeWithoutErrorBlocking is not mentioned because it never blocks. Although blocked by different mechanisms read/readNonBlocking/readLatest behave the same: + +- 2.3.5 writeWithoutErrorBlocking is not mentioned because it never blocks. Although blocked by different mechanisms read/readNonBlocking/readLatest behave the same: - read is either the second read call which is expected to deliver the next value, or any of the three are still waiting for the initial value. In any case they have to (re-)try reading. -- 2.4.2.1 Basically after each update of the recovery accessor there has to be a valid write, or an exception has to be reported to the DeviceModule, to make sure the value is seen by the device (unless the recovery accessor is updated before this happens). +- 2.3.5.1 Basically after each update of the recovery accessor there has to be a valid write, or an exception has to be reported to the DeviceModule, to make sure the value is seen by the device (unless the recovery accessor is updated before this happens). + - 2.5.1 increment the DataFaultCounter. See \link spec_dataValidityPropagation \endlink. - 2.5.3 The RecoveryAccessor has been updated before the failed write attempt and will write the value once the device has recovered. - 2.6.2 The backend has to take care that all operations, also the reads with "waitForNewData", terminate when an exception is thrown, so recovery can take place (see 3.6). @@ -97,7 +113,8 @@ If an input variable is in the error state, it sets the DataValidity flag for it - The first call to hasNewData() returns true if an error occurred in the read transfer. <c>wait()</c> will return immediately with DataVality::faulty. - The second call to hasNewData() will return false until the device has recovered and there actually is new data. <c>wait()</c> will bock until then. -\section exception_handling_implmentation Implementation + +\section spec_execptionHandling_implmentation_details Implementation details <b>3. DeviceAccess</b> @@ -169,13 +186,16 @@ The counter is increased while holding the lock 4.2.3, and then the lock is rele - 5.3.1.1 Writes to the recovery accessor before initiating the transfer (implements 2.4.2) in doPreWrite() - 5.3.1.2 Decorates doPreWrite to acquire the shared lock described in 4.2.3, then increase the transfer counter and release the lock. - 5.3.1.3 Decorates doPostWrite to decrease the transfer counter 4.2.6 - - 5.3.1.2 Blocking writes wait in doPostWrite() until informed by the DeviceModule that the device has recovered (via 4.2.2, implements 2.5.3 for writing) + - 5.3.1.4 Blocking writes wait in doPostWrite() until informed by the DeviceModule that the device has recovered (via 4.2.2, implements 2.5.3 for writing) + - 5.3.1.5 If doReadTransferNonBlocking()/doReadTransferLatest() must return true even in case of an exception, because eventually - 5.3.2 Reading - 5.3.2.1 Decorates doPreRead to acquire the shared lock described in 4.2.3, then increase the transfer counter and release the lock. - 5.3.2.2 Decorates doPostRead to decrease the transfer counter, then perform the delegated call to postRead, which might throw, and catch here. - 5.3.2.2 Blocking reads, or reads which have not seen a valid initial value yet, wait in doPostRead() until informed by the DeviceModule that the device has recovered (via 4.2.2, implements 2.5.3 for writing), the try a complete read cycle (incl. preRead) until they can successfully read a value (they might receive data with the faulty flag turned on by the sender, which is ok. It is a valid transfer). - -\subsection exception_handling_impl_details Implementation details + - 5.3.3 Sequences of calles to the delegated preXxx(), xxxTransferYyy() and postXxx() must always follow the DeviceAccess TransferElement specification. + - 5.3.3.1 preXxx() and postXxx() must alwas be called in matching pairs. If a recovery is started in doPostXxx(), the failed transfer must be finished first by calling postXxx() and DeviceModule::stopTransfer(), then a completely new cycle (including DeviceModule::startTransfer() and DeviceModule::stopTransfer()) must be initiated. + - 5.3.3.2 If the transfer is not taking place at all (because in preXxx() the device is already known to be broken and no recovery shall be attempted), the delegated preXxx() and postXxx() functions must not be called as well. + <b>6. TriggerFanout and ThreadedFanOut </b> @@ -235,7 +255,7 @@ As a consequence a copy has to be created whenever the data is written to the de - 10.4 All valid recovery accessors are written each time the device has been (re)-opened, after the initialisation handlers have been executed. If a recovery accessor has not seen an initial value yet, the version number is still nullptr, and the accessor is invalid. These accessors are not written. (implements 1.l) -<b>11. Known Issues</b> +\section spec_execptionHandling_known_issues Known issues - 11.1 In step 2.1: The initial value of deviceError is not set to 1. diff --git a/doc/main.dox b/doc/main.dox index a6dc3a9740c625f2324d2919583ff98463d4c2d7..62a892e397178f2c5b9320d73182ead5eb9d35dd 100644 --- a/doc/main.dox +++ b/doc/main.dox @@ -18,7 +18,7 @@ Examples: Technical specifications: - \subpage spec_initialValuePropagation -- \subpage exceptionHandlingDesign +- \subpage spec_execptionHandling - \subpage spec_StatusAggregator */ diff --git a/doc/spec_initialValuePropagation.md b/doc/spec_initialValuePropagation.md index 150ad98f3ba35a4d8654d4a2fe274e3806256cab..28c1771a8b768b10daa6308900fe0cafe5cf6678 100644 --- a/doc/spec_initialValuePropagation.md +++ b/doc/spec_initialValuePropagation.md @@ -173,6 +173,7 @@ All points are also covered by @ref exceptionHandlingDesign. - 1. is not implemented for Device implementations (only the `UnidirectionalProcessArray` is correct at the moment). - 2. is not implemented for Device implementations (only the `UnidirectionalProcessArray` is correct at the moment). + - Exceptions are currently thrown in the wrong place (see implementation section for the NDRegisterAccessor). A possible implementation to help backends complying with this rule would be: - Introduce non-virtual `TransferElement::readTransfer()` etc, i.e. all functions like `do[...]Transfer[...]()` should have non-virtual pendants without `do`. - These new functions will call the actual `do[...]Transfer[...]()` function, but place a try-catch-block around to catch all ChimeraTK exceptions