Cancel/Stop a running single frame exposure

Discussions on extending SharpCap using the built in Python scripting functionality
Post Reply
aldebaran99
Posts: 9
Joined: Mon Feb 26, 2024 10:03 am

Cancel/Stop a running single frame exposure

#1

Post by aldebaran99 »

Hi,

my script does a sequence of single frame exposures of e.g. 120 seconds, here the relevant lines:

Code: Select all

filename="D:\\SCTests\\testexposure.fits"
exptime=120000
SharpCap.SelectedCamera.Controls.Exposure.Value=exptime
SharpCap.SelectedCamera.CaptureSingleFrameTo(filename)
Now sometimes I need to stop/cancel a running exposure, from the script.
How can I do this?

A simple "SharpCap.SelectedCamera.CancelSingleFrame" is pointless if performed after the execution of "CaptureToSingleFrameTo".
If I do "CaptureSingleFrameToAsync" instead I can interrupt it by "CancelSingleFrame", but now the status of the exposure
("SelectedCamera.Capturing") is always false. I would need the status to make the script wait for the exposure to be ready before it continues, in case I do NOT want to interrupt.
What am I doing wrong? Do I have to go deeper into multithreading?

Matt
User avatar
admin
Site Admin
Posts: 13349
Joined: Sat Feb 11, 2017 3:52 pm
Location: Vale of the White Horse, UK
Contact:

Re: Cancel/Stop a running single frame exposure

#2

Post by admin »

Hi Matt,

the non 'Async' version of the call will wait until the capture is complete before returning, so by the time it returns it is too late to cancel. In theory you could call the cancellation API from a different thread, but I think the async version may be the better bet...

However, I am struggling myself to get a working version of this using either the 'Cancel' call or passing a cancellation token to the async method - the 'Cancel' button you see in the notification bar works a slightly different way, and neither of the two options from scripting seem to be able to simulate it. I will have to investigate further.

cheers,

Robin
aldebaran99
Posts: 9
Joined: Mon Feb 26, 2024 10:03 am

Re: Cancel/Stop a running single frame exposure

#3

Post by aldebaran99 »

Hi Robin,

at the moment I stop a running async-exposure with StopCapture()

Code: Select all

import sys
import time

filename="D:\\SCTests\\testexposure.fits"
exptime=8000
SharpCap.SelectedCamera.Controls.Exposure.Value=exptime
SharpCap.SelectedCamera.CaptureSingleFrameToAsync(filename)
time.sleep(3)  # this is a placeholder for verifying a condition, if it's met, the next two lines are executed
SharpCap.SelectedCamera.StopCapture()
sys.exit()
# further code
This is working, as far as I can see.
"time.sleep" here is a placeholder for code, that is checking whether a cancellation is called or not.
I don't mind the exception and Traceback message (these cancellations are not happening often).
Just before "sys.exit()" I would call a second script, that will wait for new orders (namely to restart the exposures).

But if no cancellation is needed (this the normal case!), the script has to wait until the Async capture is terminated.
In C# (with Maxim) I constantly monitor a property of the camera object to find out, but here I just don't succeed.

Regards
Matt
User avatar
admin
Site Admin
Posts: 13349
Joined: Sat Feb 11, 2017 3:52 pm
Location: Vale of the White Horse, UK
Contact:

Re: Cancel/Stop a running single frame exposure

#4

Post by admin »

Hmmm,

having had a look at the code there are a couple of places where the cancellation wasn't handled correctly in some cases, meaning that you could either get an error showing (rather than a cancelled message) or the frame actually captured in spite of being cancelled, depending on timings. I will fix both of those for the next update.

There is a more interesting issue though with the use of the Async methods from scripting... The async methods often involve work being done on more than one thread in SharpCap - as the work is completed the notification comes back to the original thread that kicked off the async operation. That's an issue for scripting, because the starting thread is going to be busy if the script is still running - unable to receive that notification, so from the point of view of scripting, the operation may seem to never complete or never cancel. It will tend to work from the interactive portion of the console, since while that is waiting for you to type, it can receive the notifications, but not for running a script.

I'm reluctant to try to work around this in the SharpCap code, as it would be quite intrusive and run the risk of breaking things for the 99.9% of people who are not using scripting. Instead, there is a workaround to use inside a script - run a function called 'Application.DoEvents' at any point where you are waiting for an async operation to complete. A simple example is as follows

Code: Select all

clr.AddReference("System.Threading")
clr.AddReference("System.Windows.Forms")
from System.Threading import CancellationTokenSource
from System.Windows.Forms import Application
from time import sleep
# A cancellation token source lets you cancel operations - you pass the token to the thing you might want to cancel and call 'Cancel' on the source
cts = CancellationTokenSource()
# CapturingSingleFrame is the property to check for single frame capture in progress - not Capturing
print (SharpCap.SelectedCamera.CapturingSingleFrame)
# Pass the cancellation token, and 'False' as the last parameter ensures that the next frame to arrive is saved, not the frame currently being viewed
task = SharpCap.SelectedCamera.CaptureSingleFrameToAsync("f:\\bbb.png", cts.Token, False)
# Will now report True
print (SharpCap.SelectedCamera.CapturingSingleFrame)
sleep(2)
Application.DoEvents();
# Request cancellation via the token source
cts.Cancel()
# Without this DoEvents, the cancellation does not complete correctly or update the status of CapturingSingleFrame
Application.DoEvents();
# Should now pring False
print (SharpCap.SelectedCamera.CapturingSingleFrame)

In general, if you are waiting for something to async to complete, run a mixture of Application.DoEvents() and short 'sleep' statements. You can either look at SharpCap properties (like CapturingSingleFrame) as shown above, or look at the Status of the task returned by the async operation - that will go through the stages shown here (https://learn.microsoft.com/en-us/dotne ... ew=net-8.0) to end up in RanToCompletion/Cancelled/Faulted

cheers,

Robin
aldebaran99
Posts: 9
Joined: Mon Feb 26, 2024 10:03 am

Re: Cancel/Stop a running single frame exposure

#5

Post by aldebaran99 »

Hi Robin,

thanks for your explanations, this is most interesting.
I was not aware of the fact, that here a notification might not be received because a process is too busy.
I remember "Application.DoEvents" from other situations, where a WindowsForm can be prevented from being locked this way.
The snippet you provide gives me good hints how to proceed, I will try to adapt it to my script.

I just connected a camera and run the snippet, but at the end:
> # Should now pring False
> print (SharpCap.SelectedCamera.CapturingSingleFrame)
I get not printed "false", but "true", though the capture had been cancelled.

By the way, is this
viewtopic.php?t=4527
still valid?
I mean the lines:
> If you want to do something when the action completes, do *NOT* try to use the .Wait() or .Result methods on the returned
> task - if you do your script will almost certainly freeze SharpCap in a deadlock.

Regards
Matt
User avatar
admin
Site Admin
Posts: 13349
Joined: Sat Feb 11, 2017 3:52 pm
Location: Vale of the White Horse, UK
Contact:

Re: Cancel/Stop a running single frame exposure

#6

Post by admin »

Hi Matt,

the issue with your test printing True not False is a result of one of the internal issues with cancellation handling that I mentioned - the reset of the flag to false was being bypassed. That will be fixed in the next update.

The advice not to call .Result or .Wait() on a task still stands - it will likely deadlock as the task will need to transition back to the calling (scripting) thread and cannot do that unless the calling thread is running a windows message loop. When you are running python code or call .Result or .Wait() then you are not running a message loop. The call to DoEvents() runs a message loop briefly, enough to process the task completion back to the scripting thread.

I did add a helper method if you just want to wait for a task to finish...

Code: Select all

SharpCap.SafeWaitForAsync(task) #  no return value

or

result = SharpCap.SafeGetAsyncResult(task) # returns something that you want to get hold of
These calls will internally loop, calling 'DoEvents' until the task completes (or fails - in which case the calls will throw an exception).

cheers,

Robin
aldebaran99
Posts: 9
Joined: Mon Feb 26, 2024 10:03 am

Re: Cancel/Stop a running single frame exposure

#7

Post by aldebaran99 »

Hi Robin,

using the hints and code snippets you gave me I meanwhile managed to write a set of scripts that can do what I want:
execute sequences of exposures, that can be started and stopped from the "outside", including from another machine.

For monitoring when "CaptureSingleFrameToAsync" has finished I rather use "CapturingSingleFrame",
since "SafeGetAsyncResult" was not always reliable. I don't know why, maybe my mistake.
A lot of lines now consist of "Application.DoEvents()" ;-)
This seemed to be the key for "CapturingSingleFrame" to work as expected.

For cancelling an exposure if needed I use your proposal with the cts.token, but I did not have problems
with "SelectedCamera.StopCapture()" either.

Thanks a lot for your help!

Regards
Matt
Post Reply