Page 1 of 1

BEFORE SEQUENCE FINISH DO behavior is - inconvenient

Posted: Sun Jul 21, 2024 7:12 pm
by lowenthalm
Ran into a circular logic loop that I couldn't escape using "BEFORE SEQUENCE FINISH DO" blocks. I assume this is for cleanup when errors occur, which is what I have been using it for (stopping guiding, homing the mount and such). Apologies at the start for the long-winded post - this is complicated.

Firstly, this doesn't work:

SEQUENCE
DEF SUB ErrorHandler
PROMPT "Error handler entered hit ok to continue" ENABLECONTROLS True
END SUB
DEF SUB ErrorHandlerDisable
BEFORE SEQUENCE FINISH DO
GOSUB ErrorHandler
END DO
END SUB
DEF SUB ErrorHandlerEnable
BEFORE SEQUENCE FINISH DO
END DO
END SUB
GOSUB ErrorHandlerEnable
....more code
GOSUB ErrorHandlerDisable
END SEQUENCE

It seems that error handler blocks can not be created within a subroutine. I think I understand the logic why this is done this way as its about the sequence not the subroutine (since the subroutine might be in some imported sequence - messy). This is the setup for not being able to escape the following problem:

Test sequence:

SEQUENCE
DEF SUB ErrorCleanup
PROMPT "Error cleanup entered - hit ok to continue" ENABLECONTROLS True
END SUB
BEFORE SEQUENCE FINISH DO
GOSUB ErrorCleanup
END DO
RUN SEQUENCE FROM "\\Mac\Home\Desktop\SharpCap Captures\__Sharpcap Sequences\_Test Sequences\SEQ Error Handler 2.scs"
PROMPT "We are now back in the main sequence" ENABLECONTROLS True
END SEQUENCE


SEQ Error Handler 2.scs code is just:

SEQUENCE
PROMPT "We executing the second sequence file. Hit ok to return to main sequence" ENABLECONTROLS True
END SEQUENCE


When I run this test sequence, I get the following series of prompts:

1) Prompt dialog "We executing the second sequence file. Hit ok to return to main sequence"
2) Prompt dialog "Error handler entered - hit ok to continue"
3) Prompt dialog "We are now back in the main sequence"
4) Prompt dialog "Error handler entered - hit ok to continue"

So, my the end of sequence gets called after ANY sequence that is executed, rather than just when the top level sequence ends. So, the only way to safely use BEFORE SEQUENCE FINISH DO blocks is to encapsulate any code you use in other sequences in SUBs and import them to prevent inappropriately triggering the current BEFORE SEQUENCE FINISH DO block.

It seems to me that since BEFORE SEQUENCE FINISH DO blocks were implemented in such a way that they can not be defined inside a function, my guess is that they were intended to only apply to the sequence the BEFORE SEQUENCE FINISH DO was defined in, so each sequence executed can have its own finish up block independent of all others. As it is now, its just a mess, with blocks getting executed at inappropriate times. The only current work around is to undefine and redefine BEFORE SEQUENCE FINISH DO blocks before/after running another sequence, which also gets messy very fast.

Its seems it should be farly easy to run afoul of the end of sequence block problem, but I did it in style:

I had some Python code that created a short SEQUENCE in a file (call it "File A.scs") with a standardized name and location that then gets executed from the sequence that ran the Python code that generated "File A.scs". This ran smack into the issue described above, of course. I switched to defining functions in "File A.scs". However, this has its own problems as then I have to execute the sequence-generating python code at just the right time before the importing "File A.scs" so I can all the subtroutines defined therein. And just to make it all more complicated, sequence imports can't be done from within a subroutine (which is totaly understandable), but rather are only honored if they are at the lowest scope within a sequence. Luckily, imports aren't performed all at once at the start of a sequence, but rather as the import commands are encountered during sequence execution, or I would really be up a creek.

So, to summarize:
1> The whole mess above would be avoided if BEFORE SEQUENCE FINISH DO blocks only apply within the sequence within which they are defined.
2> When a sequence file is started, its BEFORE SEQUENCE FINISH DO blocks is set to empty, and restored to empty when it completes (after executing the BEFORE SEQUENCE FINISH DO block that was active when the end of sequence was reached).
3> When a sequence file runs a second sequence, its current BEFORE SEQUENCE FINISH DO block is saved prior to the second sequence executing and restored after the second sequence completes executing.

As it is now, some surprising behaviors can happen - I may have actually run into them and branched around with by code re-write and not understood what was actually going amiss.

Re: BEFORE SEQUENCE FINISH DO behavior is - inconvenient

Posted: Mon Jul 22, 2024 1:18 pm
by admin
Hi,

interesting... not something I had explicitly thought about when designing this (ie how the two different features of the sequencer would interact with each other).

The behaviour arises because the 'RUN SEQUENCE FROM' step effectively runs the other sequence as a fancy subroutine rather than as a completely separate sequence. In general this is desireable as it means that the included sequence runs in the same context - sequencer configuration and data - as the main sequence. I think that this is necessary, since the context holds things like information on connection to guiding applications, saved co-ordinates, focuser positions, etc. Seperating out the two to make them independent would lead to a different set of problems with the inability to share those things, so neither design is ideal.

From my perspective, the 'BEFORE SEQUENCE FINISH DO' step is working as it should - it's designed to be the last thing that runs before the sequencer gets to the end of the code. What you have uncovered though highlights a gap in error handling at the block level I think.

If you have a block of various steps in the sequencer, you can currently ignore errors from it (meaning the sequencer will then move on to following steps even in the event of an error). What you can't do is set up things to happen in these cases

1) If an error occurs - not ignoring the error, but trying to do some tidy up before moving on with handling the error
2) regardless of if an error occurs, again allowing the error to propagate if there is one

In programming terms these would be try { <STUFF> } catch { <STEPS> } and try { <STUFF> } finally { <STEPS> } . I think with those you could build the things you are after without needing the 'BEFORE SEQUENCE FINISH' in the nested sequence.

I have tried to avoid adding those so far, largely because the UI (as it stands) does not have the ability to handle a block with *two* sub-lists of other blocks (one for the main steps, one for what to do in the catch/finally blocks). I might have to bite the bullet and implement that, or I could dodge the issue slightly and make it so that the catch/finally steps have to be a subroutine.

Before I dive in to start thinking about implementing this, do you agree that it would provide a workable solution?

cheers,

Robin

Re: BEFORE SEQUENCE FINISH DO behavior is - inconvenient

Posted: Tue Jul 23, 2024 2:47 am
by lowenthalm
1) If an error occurs - not ignoring the error, but trying to do some tidy up before moving on with handling the error
2) regardless of if an error occurs, again allowing the error to propagate if there is one
Yes, you can call an exception handling function, but then you can't rethrow an error up the chain. I have hacked this by either executing Python in the error handling SUB to raise an error, or, he-he, calling a non-existant subroutine like: GOSUB You_got_an_error_buddy__guiding_didnt_start, which is then caught by the next error handler block up the chain. A formal throw error statement that accepts a text string in the SEQUENCER language would be a solution to this. I think this would be enough to get by without the need to implement a 'finally' block. You've got the try and the catch and just need a THROW ERROR "error text string" and maybe also a THROW LAST ERROR that can be used in the error handler function. I think a finally block would be more language technology than needed in the context of the sequencer language. One other thing I have noticed is there is no HALT SEQUENCE command, but maybe this should always be done by throwing an error?

I initially used the 'BEFORE SEQUENCE FINISH DO' block for error handling, because I wasn't sure how well I could make the 'IGNORE ERRORS FROM ONERROR RUN' block work in practice. Now that I have more practice with the sequencer error handling mechanism and how to hack it to allow re-throwing errors, I have been relying upon it more. I think what I originally used the 'BEFORE SEQUENCE FINISH DO' for, I could switch over to using the error handling. However, the way 'BEFORE SEQUENCE FINISH DO'' behavior, in my mind, is still unexpected and probably needs some attention as well.

As it turns out, with further testing, I found that if you run steps in another sequence, and that sequence defines an 'IGNORE ERRORS FROM ONERROR RUN', that block is installed permanently, and will be called when both that sequence ends AND AGAIN when the the original sequence ends. This is very unexpected, given how you described that running steps in another sequence file behaves like calling a subroutine and 'IGNORE ERRORS FROM ONERROR RUN' can not be installed inside a subroutine.

So, my thoughts in summary:
1) Implement a formal THROW ERROR "error text string" and THROW LAST ERROR.
2) At a minimum, I think the 'BEFORE SEQUENCE FINISH DO' block should be executed only once, when the last SEQUENCE terminates, just before control is by the Sequencer to the normal SharpCap operations. Having this block get called more than once does not make sense. Then all other error handling would and could be handled using the IGNORE ERRORS blocks, the error handling function it calls and the THROW ERROR and THROW LAST ERROR statements that can be executed within that error handler.