# Terminate DICOMRetrieve move/get following an event

**URL:** <https://discourse.slicer.org/t/terminate-dicomretrieve-move-get-following-an-event/24639>\
**Category:** Support\
**Tags:** dicom, python\
**Created:** [August 4, 2022, 12:00pm UTC](https://discourse.slicer.org/t/terminate-dicomretrieve-move-get-following-an-event/24639 "2022-08-04T12:00:29Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![dvbower](https://avatars.discourse-cdn.com/v4/letter/d/90db22/32.png) [@dvbower](https://discourse.slicer.org/u/dvbower)\
**Post date:** [August 4, 2022, 12:00pm UTC](https://discourse.slicer.org/t/terminate-dicomretrieve-move-get-following-an-event/24639/1 "2022-08-04T12:00:29Z")

</div>

In a python script, I would like to terminate `ctk.ctkDICOMRetrieve.moveSeries(studyinstanceUID, seriesinstanceUID)` when an event is triggered. In short, I can trigger the event within `slicer.dicomListener.fileAddedCallback`, but the question is then how to terminate the call to `moveSeries`. Multiprocessing within slicer does not behave as expected (see [Inconsistent handling of multiprocessing by slicer - #3 by dvbower](https://discourse.slicer.org/t/inconsistent-handling-of-multiprocessing-by-slicer/24623/3)) and I’m trying to build on existing infrastructure provided in `DICOMLib` etc. to avoid re-inventing the wheel and also to minimise code maintenance.

I’ve dug around in the ctk library (`ctkDICOMRetrieve.cpp`) but I currently don’t fully understand the python wrapping well enough to know if it is possible to send a terminate command whilst a wrapped DICOM command is running. DICOMRetrieve, DICOMQuery seem to be wrapping the cpp library directly, whereas the DICOMListener class lives in DICOM.py. Thanks.

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [August 4, 2022, 3:51pm UTC](https://discourse.slicer.org/t/terminate-dicomretrieve-move-get-following-an-event/24639/2 "2022-08-04T15:51:40Z")

</div>

The dicom networking (dimse) support in CTK and Slicer only handle a set of common use cases and canceling a move never came up before that I know of. The underlying dcmtk code has a `DcmSCU::sendCANCELRequest` method that could be added to the CTK code if your use case motivates you or anyone to dig in at that level. Cancel was only implemented in CTK for CGET and CSTORE because in those cases the CTK code handles the full transaction whereas in CMOVE the transaction is initiated but then handled by the server. The cancel operation would require keeping track of the context ID to issue the cancel request. There would need to be some details to work out but implementing the feature would be pretty mechanical. It would take a while to implement, test, update Slicer, etc.

You are correct that the listener is implemented differently for a few reasons. For example, the `storescp` executable already provided all the needed functionality, the slicer integration could be done in python, and keeping the networking isolated in a separate process is more robust, easier to debug, and avoids blocking the application during network operations.

Another option might be for you to use CGET if you need to be able to cancel the operation. It used to be that CMOVE was more universally supported but I believe CGET is pretty common these days.

Working with CMOVE, and dimse in general, is always a challenge, but it’s a robust standard that has held up well in the field so if you can work this out it should be reliable. If you have flexibility in terms of the server you work with then you may prefer DICOMweb which is a lot easier for most programmers to work with. If you describe your constraints maybe we can give more specific advice.

---

<div class="post-metadata">

**Author:** ![dvbower](https://avatars.discourse-cdn.com/v4/letter/d/90db22/32.png) [@dvbower](https://discourse.slicer.org/u/dvbower)\
**Post date:** [August 5, 2022, 7:59am UTC](https://discourse.slicer.org/t/terminate-dicomretrieve-move-get-following-an-event/24639/3 "2022-08-05T07:59:51Z")

</div>

Thanks Steve for the detailed response. I will certainly look into these options for a long-term solution. In the short-term, however, I’m wondering if I can simply raise a custom exception in `slicer.dicomListener.fileAddedCallback` to return programme control back to python `main`. But I worry this might result in orphaned or zombie processes (associated with CMOVE) that won’t get cleared until the python script completes (exits). Nevertheless, maybe this will suffice in the short-term.

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [August 5, 2022, 12:42pm UTC](https://discourse.slicer.org/t/terminate-dicomretrieve-move-get-following-an-event/24639/4 "2022-08-05T12:42:27Z")

</div>

I think the key problem is that a CMOVE request tells the dicom server to start a transfer operation and unless you tell it to cancel it may do multiple retries or otherwise get in a bad state if you stop the listener. I suppose it would eventually time out and just log an error but that’s probably not preferred. Maybe it’s easier to just let it send the data but ignore everything when it’s no longer useful to you.

I’m not clear on the control flow issues you mentioned since the process should be all be event-driven.

Can you describe a bit more about the scenario you are trying to support?

---

<div class="post-metadata">

**Author:** ![dvbower](https://avatars.discourse-cdn.com/v4/letter/d/90db22/32.png) [@dvbower](https://discourse.slicer.org/u/dvbower)\
**Post date:** [August 5, 2022, 12:50pm UTC](https://discourse.slicer.org/t/terminate-dicomretrieve-move-get-following-an-event/24639/5 "2022-08-05T12:50:18Z")

</div>

The main objective is to only download one instance associated with a given Study ID and Series ID. This is because the dicom metadata in any one instance will determine the next course of action (download all instances in that series, or skip that series, which I would perform as a separate loop once the single DICOM samples from each series have been interrogated with pydicom).

I had also thought about allowing C-MOVE to proceed but somehow mute the listener to avoid downloading any more files after the event has tripped. I’m not sure how to do this elegantly though, without effectively forcing C-MOVE to error out (which seems to happen if I corrupt the listener attributes, which is not elegant, as you rightly point out).

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [August 5, 2022, 1:25pm UTC](https://discourse.slicer.org/t/terminate-dicomretrieve-move-get-following-an-event/24639/6 "2022-08-05T13:25:54Z")

</div>

Since you are doing custom programming anyway and need to make sure it works with the functions of the particular server you are using, maybe instead of using `ctkDICOMRetrieve` you could use [`movescu`](https://support.dcmtk.org/docs/movescu.html) directly to request a single instance. Using these command line tools can help debug the basic operations and make it easier to implement a robust user interface in Slicer.

Regarding the control flow issues keep in mind that all of these networking operations are asynchronous and you may need to handle responses that come in any order.
