Jump to content

Subroutines with no Call Stack - how?


Recommended Posts

Posted (edited)

I'm slowly groking how EISY/ISY programming works.

My first learning was that If-Then-Else is really When-Then-Else. That is the If() block is called when something inside it changes (assuming your variables, if any, are State and not Integer).

The second was that programs are not re-entrant. If a program is called while it is still running, that first "instance" will terminate at the next Wait or Repeat line. No passing Go or collecting $200 - the rest of that program won't be called. Instead the new "instance" of the program runs.

After instrumenting my code with lots of UD Mobile notifications, I'm seeing that if in MyProgram1 you have: 

Run Program 'MyProgram2' (If)

Then MyProgram2 will run the If block and execute either Then or Else...BUT

The execution of MyProgram1 does NOT wait for MyProgram2 to finish executing. It's not on a Call Stack, it's just you have multiple programs running simultaneously. I assume this is the same whether calling the (If) or (Then) or (Else) blocks directly.

If I'm correct, this means that I can't use this mechanism to have subroutines. Which I'd really like to have since it prevents duplicating the same code in multiple programs. 

So, my real question is: How to have subroutines?

TIA

NOTE: Edited this post to correct the terminology. The behavior is as described and that is what is called "re-entrant." 

Edited by smorgasbord
Correction of terminology.
Posted

If your goal is to have Program 2 run "in the middle" of program 1, you can put a wait in program one, right after the "call" for a length of time you need for program 2 to complete. Just be aware that program 2 can NOT change the If state of program one in any way, or program one will either start over or run false etc, as it will be re-evaluated at the WAIT.

 

Posted

Just call program2 from program1, and if you don't want program2 to be self-triggered just disable it.

Enable/disable of any program only affects the If section triggers. The code will still run and the conditions will only act as conditions/filters.

However program1 will not wait for program2 to return.

All programs surrender their current time slice to the ISY executive logic engine, and I/O handlers, when encountering a Wait or Repeat program line, to be continued when the system "gets the chance" at the end of the programmed Wait or Repeat time.

Posted (edited)

I just ran into a related issue with a hot water recirculating pump routine. I wanted movement in a bathroom to run the pump for 2 minutes to get the hot water into the pipes. But, if the pump has been recently run (say 15 minutes), it shouldn't run because it's wasteful. So, I wrote this program:

If
        'Bathroom Motion-Sensor' is switched On
    And $RecirRanRecently is not 1
 
Then
        $RecirRanRecently  = 1
        Set 'Recir Pump' On
        Wait  2 minutes
        Set 'Recir Pump' Off
        Wait  13 minutes
        $RecirRanRecently  = 0
 
Else
   - No Actions - (To add one, press 'Action')
 

Now, given that there is no Reentrancy and no Call Stack, I understand why it fails, but don't know the best way to fix. 

The first time through it works. And the second time through it'll work, too. But, if that second time is rejected for being too soon (RecirRanRecently is 1), then first instance is terminated at one of the two Waits, and so RecirRanRecently is never reset to 0. And so the pump would never run again until reboot initializes RecirRanRecently to 0.

EDIT: I'm not using the time-out in the motion sensor hardware because I have another program that runs the pump on timed intervals (every half hour) and it uses the same RecirRanRecently variable in an attempt to get this motion program to not run soon after the timer program rang

What's the best way to accomplish this?

Edited by smorgasbord
Posted

@smorgasbord

Try making your if statement 

Bathroom Motion-Sensor' is switched On
    And $RecirRanRecently is 0

And then eliminate the first line in your THEN.  

As you have it written, when it hits the first WAIT, the IF is re-evaluated. Since you just changed the variable to a 1, it fails the IF and turns False, stopping the program.

 

Posted

RecirRanRecently is not a State variable, so the IF isn't re-evaluated. Isn't convention to put a lower-case "s" in front of State variable names?

I'm trying new program logic now, where I have double-stacked routines to perform the test and then the run. I'll post here in a little bit if it looks like it's working.

Posted
19 minutes ago, smorgasbord said:

RecirRanRecently is not a State variable, so the IF isn't re-evaluated. Isn't convention to put a lower-case "s" in front of State variable names?

I'm trying new program logic now, where I have double-stacked routines to perform the test and then the run. I'll post here in a little bit if it looks like it's working.

Ok.. that makes more sense then now..

Your motion sensor could be turning off, which would also stop the program, and then back on, restarting the program. How do you have that set up? 

Posted (edited)

OK, I ended up with 2 variables and 4 programs:

RecirRanRecently: An integer (not state) that is True (1) if pump has recently run

sOnVacay: A state variable that is True if we've set a Vacation Mode, which stops the pump from running automatically and turns lights on/off semi-randomly.

Repeat Loop(): A "Repeat While" loop that asks to run the Pump every 35 minutes.  Started at 7am and stopped at 10:30pm.

Bathroom Motion(): When the Motion Sensor detects motion, asks to run the Pump

Run If(): If Pump hasn't been run in last 15 minutes, runs the Pump routine

Run Once(): The routine that runs the pump. Only the "THEN" block is called and only from Run If(), so the Waits don't get interrupted. Uses $RecirRanRecently variable to prevent Run If() from calling it when it shouldn't. Runs pump for 1.75 minutes and then Waits for 13.25 minutes.

Run Once:
If
   - No Conditions - (To add one, press 'Schedule' or 'Condition')
Then
        $RecirRanRecently  = 1
        Set 'Recir Pump' On
        Wait  1 minute and 45 seconds
        Set 'Recir Pump' Off
        Wait  13 minutes and 15 seconds
        $RecirRanRecently  = 0
Else
   - No Actions - (To add one, press 'Action')
——
Run If:
If
        $RecirRanRecently is not 1
Then
        Run Program 'Run Once' (Then Path)
Else
   - No Actions - (To add one, press 'Action')
——
Bathroom Motion:
If
        'Bathroom Motion-Sensor' is switched On
    And $sOnVacay is not 1
    And $RecirRanRecently is not 1
Then
        Run Program 'Run If' (If)
Else
   - No Actions - (To add one, press 'Action')
——
Repeat Loop:
If
        From     6:59:00AM
        To      10:31:00PM (same day)
    And $sOnVacay is not 1
Then
        Repeat While $sOnVacay is not 1
           Run Program 'Run If' (If)
           Wait  35 minutes 
Else
   - No Actions - (To add one, press 'Action')

Run If() has two Waits that combine for a 15 minute minimum time period between successive pump runs. The Repeat Loop() program only asks for pump runs every 35 minutes. Motion in the bathroom will ask for a pump run if the 15 minute minimum time has elapsed. While Repeat Loop() only runs during waking hours, motion in the bathroom at any time can run the pump.

I thought about trying to reduce this to 3 programs instead of 4, but since Repeat Loop() might ask for a pump run soon after a Bathroom Motion requested and granted pump run I think it's needed. If we could compound the While clause of the Repeat instruction, such as:

  Repeat While ( ($sOnVacay is not 1) AND (sRecirRanRecently is not 1) )

Which isn't possible as I understand it. I could maybe create a third variable and have it be the sum of those two variables, and then have Repeat While ThirdVariable is 0, but I don't know about addition and of an integer and a state. Thoughts/ideas welcome.

Edited by smorgasbord
Posted
12 hours ago, dbwarner5 said:

Your motion sensor could be turning off, which would also stop the program, and then back on, restarting the program. How do you have that set up? 

Motion sensors set to ON Commands only, and there are no programs looking for a "switched Off." I also have about a 5 minute time-out so the motion sensor isn't constantly sending commands. 

Posted (edited)

Typically you disable the program rather than using lots of variables. Also, for flags, just use a PGM status rather than a 0/1 variable. 

 

Pgm1

If motion 

And PGM on vacation false

Then

Run then pgm2

 

Pgm2 disabled

If blank

Then

Disable pgm1

Turn on pump

Wait 15 minutes 

Enable pgm1

Turn off pump

 

 

Only caveat is if you happen to reboot during the 15 minute wait, the program will be disabled at start and be stuck. So I use a single program that runs at startup to ensure that all of those programs are set to the correct state. 

 

As a side note, programs with blank if will run true if set to run at startup. 

Edited by apostolakisl
  • 4 weeks later...
Posted
On 11/7/2024 at 12:24 PM, smorgasbord said:

The second was that programs are not re-entrant. If a program is called while it is still running, that first "instance" will terminate at the next Wait or Repeat line. No passing Go or collecting $200 - the rest of that program won't be called. Instead the new "instance" of the program runs.

I just wanted to come back and clarify that I had the terminology backwards. That is "re-entrant" means the program restarts (re-enters?) when called again. So, the programming model is re-entrant and the behavior I quoted above is what happens.

That said, there apparently is no call stack and everything called appears to run in its own context/instance/thread/process. So, for us old C programmers, calling a subroutine means that subroutine starts to run, but the calling routine also continues to run, so there are no guarantees which thing in which routine gets called first. So, this is somewhat like parallel programming. 

  • Like 2
Posted
17 hours ago, smorgasbord said:

I just wanted to come back and clarify that I had the terminology backwards. That is "re-entrant" means the program restarts (re-enters?) when called again. So, the programming model is re-entrant and the behavior I quoted above is what happens.

That said, there apparently is no call stack and everything called appears to run in its own context/instance/thread/process. So, for us old C programmers, calling a subroutine means that subroutine starts to run, but the calling routine also continues to run, so there are no guarantees which thing in which routine gets called first. So, this is somewhat like parallel programming. 

Re-entrant is correct terminology, however, around here we all call it "re-trigger".  It looks like you have a handle on this and I am sure you will succeed in programming your ISY.  There are lots of little tricks and best practices and unusual situations (like a reboot while a program is running) and the forum is a great resource to help you save time and avoid pitfalls.  

  • Like 1
Posted
10 minutes ago, larryllix said:

BTW: ISY arithmetic has logical operators in its repertoire. IIRC there is AND, OR, and XOR.

Yes, but those can only appear in the program IF construct, not as branching lines inside a program. And as I pointed out in the first post here, that IF block is auto-executed when the parameters inside it change (assuming it's not all INTEGER variables). You can force an IF to be executed via the RunIf() call, but then you've got two programs running essentially simultaneously, not in a single execution thread. 

BTW, I'm not complaining about the programming model, just asking questions, gaining knowledge and pointing out differences with "standard" programming models. I suspect I'm not the first "regular" programmer to come into this world. The ISY/EISY programming model makes sense given Automation is almost always a set of things to do when triggered by an event (which could include a time event), so I understand why these choices were made.

My only real complaint is that the program debugging tools could be better. You can see current values of STATE and INTEGER variables (but not on the same screen at the same time), and you can see which programs are currently running, and now with the Notification Polyglot plug-in you can at least get a rudimentary "printf" type output to track what was run and what the variables were at that time, but there's no step-thru debugger, call log stack, etc. 

Posted

These kinds of discussions pop up every now and then. The ISY programming language is not a general-purpose procedural language. Rather, the collection of ISY programs implement a finite state machine, where the IF conditions trigger state transitions and the statements in programs describe the actions when a transition is triggered. 

Posted
Yes, but those can only appear in the program IF construct, not as branching lines inside a program. And as I pointed out in the first post here, that IF block is auto-executed when the parameters inside it change (assuming it's not all INTEGER variables). You can force an IF to be executed via the RunIf() call, but then you've got two programs running essentially simultaneously, not in a single execution thread. 
BTW, I'm not complaining about the programming model, just asking questions, gaining knowledge and pointing out differences with "standard" programming models. I suspect I'm not the first "regular" programmer to come into this world. The ISY/EISY programming model makes sense given Automation is almost always a set of things to do when triggered by an event (which could include a time event), so I understand why these choices were made.
My only real complaint is that the program debugging tools could be better. You can see current values of STATE and INTEGER variables (but not on the same screen at the same time), and you can see which programs are currently running, and now with the Notification Polyglot plug-in you can at least get a rudimentary "printf" type output to track what was run and what the variables were at that time, but there's no step-thru debugger, call log stack, etc. 
You're spoiled by high level language tools.

ISY language takes much more creativity to debug complex programming constructs but it can and has been done.

Sent from my SM-S711W using Tapatalk

  • Like 1
Posted

The ISY language operates more like a simplified multitasking operating system than as an application. The "applications" are the individual If/then programs that you create, as well as monitor the input/output devices it can receive and send from. If you ever programmed in VB6 and created routines that send and receive data through sockets or serial ports for example, then you know that you had to sprinkle "DoEvents" commands in your code that sent data and waited for received data, to allow the operating system to do those and other tasks as you program waited for the next event. DoEvents releases control to the operating system so that it can scan for the possible events. In the ISY, it evaluates the status of each If/then program, to see if the next step is due. If so, then it initiates it...and keeps monitoring the rest of the system.
 

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...