Make decisions based on schedule/time

Hello,
I am doing something I’ve never done with the PAC Control or any program for that matter. I am new to control based on time. I need to be able to control things based on what time of day it is. In the future it may even turn into something along the lines of a sprinkler clock type application. Can anyone help me out how to do this?
I need to be able to program “at this time, do this” or "between this time and this time, do this"
Thanks!

GetSecondsSinceMidnight() is a pretty useful command for executing commands at different times of day. It is a bit more efficient than juggling separate hours, minutes, and seconds. For example, if you want to execute a command once at 6 AM, you could have a nSecondsSinceMidnight variable that is updated with that function in a loop (The “Get seconds since midnight” block in the example below), and also an nExecutionFlag variable used as a boolean. A conditional somewhere in the loop would check to see if nSecondsSinceMidnight >= 21600, which is the seconds equivalent for 6 AM, and also that nExecutionFlag has not been set yet. Once the True condition is satisfied, toggle the flag, and execute the command.

At the beginning of each day, you could reset all your flags and start over.


Inside the Check for new day optoscript block, you’d have something like this:

if (GetSecondsSinceMidnight() < nSecondsSinceMidnight) then
  //It's a new day!
  SetVariableFalse(nExecutionFlag);
endif

This is a basic example, you can get much more complicated, and add in calendar scheduling too to do different things on different days. There is a nice section on Time commands in the PAC Control user manual.

Thanks for your help. This sounds like exactly what I was looking for. I didn’t see the GetSecondsSinceMidnight() instruction available in the instruction list for the condition blocks. Sounds like the only way to use it is in an Optoscript block? I was going to use it in a condition block to make my script go one of two ways, but if it’s not there I would have to use variables to make things happen.

If I want something to update regularly on a loop is there any way to do that on this chart and not have it part of the normal flow? Or do I have to use a different chart or just put in in the flow of my current chart?
Ex: if there is a variable I want to update on a regular interval like say every 30 minutes or every minute for a task, but I don’t want it to be a part of the normal chart because of the delay block built into the chart can that move command be executed outside the normal delayed chart order?

Sounds like you could use a timer. You’ll find both “Up timer” and “down timer” data types under “Numeric Variables” in your strategy tree. For example, you could use the “StartTimer” command on an uptimer, then check in periodically to see if 30 minutes have gone by since last you checked, then do other stuff and loop back to your check.

Or, depending on the “task” you could delegate to the brain since our brains (aka I/O Units) have some I/O features that include time, such as “Start On Pulse” or “Get Period Measurement Complete Status.”

Would one of those work for you?

ok, how about this. There is a variable I want checked every minute, but if I include it in the chart just after block 0 and the chart has a 500msec delay on it won’t that add up after time? in other words, where do you normally check your variables? Sorry, I’m still new at all this.

I saw someone had a command block in one of their example charts that was just off to the side that wasn’t connected to block 0 at all. Does that work or does everything have to connect to block 0? Can you have more than one route off of block zero on one chart or just one?

Thanks

A block off to the side does nothing; not in use (perhaps just there for reference or left over or used as a comment). You’ll find in PAC Control config, under Edit > Find… there’s a “Missing Connections” option, to make sure you didn’t forget to attach anything–or have your flow chart stop flowing before you meant.

So, you have control over how often you check your variable. You can have one whole flowchart that just checks it and does whatever you need to do based on what the value is then. Don’t be shy about using lots of charts at once (up to 16 on a PAC-R, more charts on other PAC types)!

FYI, if you haven’t already, be sure to check out the handy Chapter 4: Designing Your Strategy which you’ll find in PAC Control under Help > Manuals > User’s Guide.

Thanks for the help. I will check that stuff out.

Is the GetSecondsAfterMidnight() only available in an optoscript block? I can’t use it in a condition block. I couldn’t find it in there. If it’s only in an optoscript block then I would have to use an if statement to change output variables instead of using a condition block to make it go one way or the other in the chart?

All commands you can use in OptoScript are also available in Action or Condition Blocks, and visa versa. However, you might need a temporary variable, as is the case here (and one reason why sometimes folks prefer OptoScript). You’d just use the Time/Date command: “Get Seconds Since Midnight” to put the number of seconds in a variable (in an action block), then compare the variable to something in your condition block, or compare in OptoScript if you’d like.

Here’s a slight modification to sensij’s suggested / pictured code above, where I’m assuming the block he labeled “Get Seconds Since Midnight” would put that value in, say: [B]nNewSecondsSinceMidnight[/B].

You could then compare that in a condition block or as part of an OptoScript block like he did, but I’d modify it slightly to be:


if ([B]nNewSecondsSinceMidnight[/B] < nSecondsSinceMidnight) then
  //It's a new day!
  SetVariableFalse(nExecutionFlag);
endif

nSecondsSinceMidnight = [B]nNewSecondsSinceMidnight; [/B]// reset for comparing the next time around (after delay)

I hope that helps!

This does help. Thank you.

Will this work for spans of time? Such as between 0600-1200? I want to make sure I’m getting my syntax right and also find out if there are better ways to code it.

if (nSecondsSinceMidnight > 21600) and (nSecondsSinceMidnight < 43200) then

  SetVariableTrue(nSprayFieldValve);  // opens sprayfield valve if time between 0600 and 1200

  SetVariableFalse(nExecutionFlag);

endif




// also...


if ([B]nNewSecondsSinceMidnight[/B] < nSecondsSinceMidnight) then
  //It's a new day!
  SetVariableFalse(nExecutionFlag);     //  <----what does this do?
endif

Your syntax is [I]close[/I], but that won’t actually compile (you need parenthesis around your 2 and-ed conditions to make them into one big one). Also, if it makes more sense to you, there’s this condition, which is almost exactly the same check as you showed. For extra bonus points, can anyone tell me how this condition:


( IsWithinLimits( nSecondsSinceMidnight, 21600, 43200) ) 

differs from this one?


( (nSecondsSinceMidnight > 21600) and (nSecondsSinceMidnight < 43200) )

Anyone? It’s kind of a subtle difference, and probably wouldn’t matter for this application, but in other cases… the devil is in the details!

My guess is that it would be more like including a “equal to” in your greater than or less than signs which for my purposes would work. In either of these cases a variable can replace the numbers correct?
Thanks for your help.

Yes! And I recommend using a variable instead of a literal there since:

  1. You can search for it later (vs. if it's hard-coded, then you would just have to hope you find them all manually if you make a change to those values)
  2. You can change it temporarily, like if you're stepping through your code to see if it's working as you meant

Speaking of stepping through your code, you might be interested in [U][B]this “Quick Start” video[/B][/U] (it’s 3rd in a series of 3). Which is also a good way to see what happens when you execute a particular command – like that SetVariableFalse command you asked about before (that command just sets the passed-in variable to 0, FYI).

But perhaps on that you weren’t asking what it DOES (also don’t forget the built-in help, which you can get to quickly by highlighting commands like that and clicking on the “Command Help” button in OptoScript). Maybe you meant “why is this here?”

yes, I meant why is this here? It doesn’t seem to have anything to do with the rest of the statements…

Also, I started another post about getting both total and gpm out of my high speed digital counter block. Do you think you can check that out. It’s a problem I’m trying to solve right away and if you have any example charts of how to collect meter information it would be great.

Just another question,
Do you need the outer parenthesis on your IsWithinLimits() ?

On your “why is it there?” question, that was just a flag to check elsewhere in the code. But you could put all your “do this when it’s a new day” right there instead of having a flag to check later…

The outer parenthesis are NOT needed in that case, but I like to throw extras in generously since, like white space, they don’t cost anything and sometimes make the code a little more readable!

I replied to your other post on gpm, etc., including a real-world example for you. I might be able to dig up actual code behind that example, digging now…

When I included the execution flag variable in my example, it was specifically to address this portion of the original post:

Without taking steps to make the loop timing deterministic, it is hard to guarantee that any particular action can be executed at a specific time. If you make your conditional test for equality, there is a chance you would miss the trigger. In other words, an easier implementation would be “at the first opportunity after this time, do this”.

In Optoscript, to execute an action once per day as soon as possible after a particular time, it might look something like this:

if ((nSecondsSinceMidnight > 21600) and IsVariableFalse(nExecutionFlag)) then

  SetVariableTrue(nSprayFieldValve);  // opens sprayfield valve in first loop after 0600

  SetVariableTrue(nExecutionFlag); // indicates that the action to open the valve has been executed today

endif

// also...


if (GetSecondsSinceMidnight() < nSecondsSinceMidnight) then
  //It's a new day!
  SetVariableFalse(nExecutionFlag);     //  resets the flag to indicate that action has not yet occurred today
endif

I try to avoid “new” or “old” prefixes on variables to test for differences between successive iterations, which is why I preferred to simply re-evaluate the GetSecondsSinceMidnight function in the conditional. As you pointed out, that approach requires the use of OptoScript, while Mary’s alternative can be done through action blocks. Mary’s alternative is also likely to be more efficient, since the time required in each loop to execute the GetSecondsSinceMidnight + a Move command is probably less than the time necessary to execute the GetSecondsSinceMidnight function twice.

I have one more question. In this script:


nOffTime = 21600
nOnTime = 79200

If(IsWithinLimits(GetSecondsSinceMidnight(), nOnTime, nOffTime)) then
  nSpray1RecTimeTrue = 1;
Else
  nSpray1RecTimeTrue = 0;
EndIf

Is the brain savvy enough to understand if Off is before midnight and OnTime is after?..like nOnTime = 79200 and nOffTime = 21600 because if it isn’t I will need to adjust my code. If so, what is the best practice? do I have to separate my code to test between 79,200 and 86399 (last second in day) AND then 0 to 21600 or is there a better coding practice for this?

I know I can test for the opposite such as:


nOffTime = 21600
nOnTime = 79200

If(not IsWithinLimits(GetSecondsSinceMidnight(), nOffTime, nOnTime)) then
  nSpray1RecTimeTrue = 1;
Else
  nSpray1RecTimeTrue = 0;
EndIf

But is that best if I need to start doing different on and off times every day and such. It can start to get confusing fast when you use a lot of "not"s reverse logic like that. As a novice I was wondering if there is a best practice for this.

Also, if I need to have the user be able to enter normal hours such as 06:00 what is the best way to convert that from Seconds since midnight? I know I can do a bunch of math, but I was wondering if there was an easier way.

Thanks

Nope, this is just doing simple math here, it doesn’t know you are working with time.

Yep, code needs to change - date and time handling are deceptively difficult to handle in code, especially without built-in libraries for it with date and time data types.

Here is your code modified to support spanning midnight, also added some bonus code to convert from a more human understandable format:


//nUserOnTime = 2200;
//nUserOffTime = 600;


//Convert user readable time to seconds since midnight
nOnTime = (nUserOnTime / 100) * 3600 + (nUserOnTime % 100) * 60;
nOffTime = (nUserOffTime / 100) * 3600 + (nUserOffTime % 100) * 60;


//Get the current time
nCurrentSeconds = GetSecondsSinceMidnight();


if (nOffTime < nOnTime) then //Spans midnight, need to check if either condition is true
  if (nCurrentSeconds >= nOnTime or nCurrentSeconds < nOffTime) then
    nSpray1RecTimeTrue = 1;
  else
    nSpray1RecTimeTrue = 0;
  endif
else //Does not span midnight, need to make sure BOTH conditions are true
  if (nCurrentSeconds >= nOnTime and nCurrentSeconds < nOffTime) then
    nSpray1RecTimeTrue = 1;
  else
    nSpray1RecTimeTrue = 0;
  endIf
endif

Now if you have a requirement to schedule beyond a single day, or to only operate on certain days of the week, then, of course, it gets more complicated, but keep working at it a bit at a time and you will get there.

Also, if you are doing the above routine multiple times, you may want to search in the forums here for subroutines, as this is a perfect candidate for that.

Good luck!

Could use similar flowchart for cooling tower thnx Bob