Scheduling an event


#1

Hi, I’m completely new to PLC and opto22. My main experience in programming is PHP.
I’m currently trying to build a feeding system and am starting with a scheduler.
I need the following:

  1. to have a user adjustable feeding period which allows the user to define the hours of the day in which a feeder can operate. E.g. between 7 am and 7pm.
  2. allow the user to set the number of times a day the feeding can happen.
  3. allow user to set the number of feed ‘shots’ to emit per feeding.

To start with “1”, I’m trying to populate a variable that will house the time, then compare that to the current time to determine if feeding is within the allowed feeding time. Can you give me some guidance as to the best way to do this?

Thanks!


#2

Not sure if you have already figured out what you needed, but for what it is worth, here are my ideas. I deal with scheduling on Opto for a lot of HVAC/occupied time stuff.

You can store time values as integer tables so that they are compatible with the Opto built-in time functions (kind of an Opto date/time data type).

I also started taking advantage of the NTP Timestamp conversion functions whenever I want to do “math” on a date/time. That way I can simply add and subtract integers (bit shifted correctly) and then convert back to the table format when done. Here is the Optoscript for a AddMinutesToTimeTable subroutine I have:

DateTimeToNtpTimestamp(ntTimeTable, nNTPTime); //Convert the table time to a single 64 bit NTP timestamp
nAdd = (nMinutes * 60); //Convert my minutes to seconds
nAdd = nAdd << 32; // Bit shift this over past the NTP timestamp fractional second part
nNTPTime = nNTPTime + nAdd; //Add this to the timestamp
NtpTimestampToDateTime(ntTimeTable, nNTPTime); //Convert back to Opto date time format

[SIZE=2]The same approach can be used for greater/less than comparisons.

On the other hand, since you may not care about the date part of the date-time, you could just store seconds since midnight in an integer since there is a built in function for this as well. You would then need to convert from your HMI to your seconds storage format and back, but the math gets a whole lot easier in your strategy when you are working with a single integer rather than a table of integers.

[/SIZE]


#3

Thanks Philip! So far I have the following:
Chart: feedPeriodStartTime
optoscript:
//Get readable time layout
iFeed_period_start_hours = iFeed_period_start / 3600;
iFeed_period_start_minutes = (iFeed_period_start % 3600) / 60;
iFeed_period_start_seconds = (iFeed_period_start % 60) % 3600;

numberToStringField(iFeed_period_start_hours, 2, feed_period_start_hours);
numberToStringField(iFeed_period_start_minutes, 2, feed_period_start_minutes);
numberToStringField(iFeed_period_start_seconds, 2, feed_period_start_seconds);

if (iFeed_period_start_hours < 10) then
feed_period_start_hours[0] = ‘0’;
endif
if (iFeed_period_start_minutes < 10) then
feed_period_start_minutes[0] = ‘0’;
endif
if (iFeed_period_start_seconds < 10) then
feed_period_start_seconds[0] = ‘0’;
endif
feed_period_start = feed_period_start_hours + “:” + feed_period_start_minutes + “:” + feed_period_start_seconds;

Chart: feedPeriodStopTime
optoscript:
//Get readable time layout
iFeed_period_stop_hours = iFeed_period_stop / 3600;
iFeed_period_stop_minutes = (iFeed_period_stop % 3600) / 60;
iFeed_period_stop_seconds = (iFeed_period_stop % 60) % 3600;

numberToStringField(iFeed_period_stop_hours, 2, feed_period_stop_hours);
numberToStringField(iFeed_period_stop_minutes, 2, feed_period_stop_minutes);
numberToStringField(iFeed_period_stop_seconds, 2, feed_period_stop_seconds);

if (iFeed_period_stop_hours < 10) then
feed_period_stop_hours[0] = ‘0’;
endif
if (iFeed_period_stop_minutes < 10) then
feed_period_stop_minutes[0] = ‘0’;
endif
if (iFeed_period_stop_seconds < 10) then
feed_period_stop_seconds[0] = ‘0’;
endif
feed_period_stop = feed_period_stop_hours + “:” + feed_period_stop_minutes + “:” + feed_period_stop_seconds;


Not sure if using ‘charts’ is the correct way to do this in opto. I have way too high CPU usage by the pattern I’ve layed out so I’ll probably have to rethink it.


How to create and include a strategy of calendar and time
#4

Make sure you put delays in your charts as they will keep on running, you have the equivalent of:

while(1)
{
//chart stuff is here
}

What you want is:

while(1)
{
//chart stuff is here
sleep(1000);
}

Throw in a DelayMsec(nDelayMS); in your charts and your CPU issue will go away. Also, you should be able to run all that in one chart.


#5

Hello tidan,

I’m concerned about your last statement here – are you using SoftPAC (and therefore can easily see how your charts impact your CPU usage)? Are you familiar with our “Delay” commands, which should be included in any/all looping charts with a reasonable/appropriate delay for whatever your logic is doing? (E.g. in this OptoScript it looks like you don’t need to update faster than once every second, so you could delay for, say, half a second/500ms perhaps.)

BTW, there’s a selection of timing/date related examples here near the bottom of our “Greatest Hits” code samples: Greatest Hits - and mini-lessons

-OptoMary


#6

Yes! What he said! Gold star for philip!


#7

Thanks for the tips Philip and Mary.
I’m not sure I’m laying out my charts correctly, and this may be contributing to the CPU usage as well. I’ll add the delays in and see where that gets me. Also, here are some screenshots of my main charts - does this layout look correct?





#8

I think you may have some style issues, but that looks functional. Here is a typical pattern (very simple) I follow for looping charts:


I never put anything in Block 0.


#9

Okay. If I want to use a ‘global’ type function that I can insert as a delay, i.e. a user function of sorts, that I can reference how would I go about doing that? Currently I’m using an i var referenced in an action block but I’d like to be able to have global-effective arguments for delay time.

BTW, setting a 100msec delay totally did the trick to drop CPU usage. Now showing 0~4%. Thanks a bunch!


#10

I wouldn’t create a subroutine for that - too simple of a task.

Since all variables are global in PAC Control (except for the special case of subroutines, which I don’t think you are using at this point), if you declare an integer called iMyLoopDelay and use this in your call to Delay(), it will change the delay time whenever you modify the iMyLoopDelay variable.

You can use this variable in all your loops/charts where this delay is appropriate.

So basically put in a Delay(iMyLoopDelay) where ever you need a chart loop delay. Then adjust iMyLoopDelay’s value appropriately.


#11

Thanks Philip, that is exactly what I did and it will work for now.
I have my HMI working now so the user can set the feed period start and stop time and I cleaned up my loops so that charts aren’t getting called on each iteration as well as inserted delays on every loop. Now I need to figure out when its applicable to use seperate charts vs. putting what I have now into one chart…guess I’m not really clear on how the chart system is best utilized?!


#12

Hi tidan,

You might find this: “Best Practices” technical note (click here) helpful. Many of the ideas and suggestions in there come from our long-time users who wished they known these little details/tricks when they first got started. In particular, there’s a section called “Building Strategies: PAC Control” which talks about what I like to call “flowchart flow.”

Hope that helps!

-OptoMary


#13

Great read. Thanks Mary!
I’m currently challenged by trying to figure out the logic for adding my feed ‘shots’ during my feed period.
I have the user-adjustable feed period working well, but now I must add a feature that will allow the user to dictate how many feedings (AKA feed shot) occur during that user specified feeding period. Any ideas on the logic?
So far I’ve created another chart and using optoscript populated an integer var with (feedPeriodStop - feedPeriodStart) to get the number of seconds for the feeding period, then divided that by the number of feed shots the user may enter (e.g. 3). Now I don’t know where to go from here?!
Thanks again!


#14

Hi tidan,

I’m not sure I understand what you’re looking for here. Could you tell me what the output is? (Will you turn on a digital output or something which will let the food flow? Or perhaps you’re controlling an analog output?)

You mentioned your PHP background, can you give an example of how you might code something similar in PHP?

I suspect you might have more charts than you need, perhaps just one chart that loops checking the time and various user inputs would be sufficient here. Many of our users only ever use 1 chart (multi-threaded programming is actually a pretty complicated concept – not unlike patting one’s head and rubbing one’s tummy when in many cases you can just pat-rub-pat-rub without having to multi-task!)

Please forgive the goofy analogy.

-OptoMary


#15

Well, my feeding pattern utilizes a user configurable feed ‘period’ which sets the time during which an actual feeding CAN occur. The actual feed shot is a signal sent out to the feeder to send a burst of feed into a tank (providing its within the said feeding period).
I haven’t thought how I’d write this in PHP, but could probably come up with something creative.
Basically, I need to come up with the math/logic to figure out how to implement user designated feed ‘shots’ during the user designated feed period. E.g. if feeding period happens to be set between 07:00 and 19:00 (7 to 7) and the feed ‘shots’ during that period are, say 3, what would be the easiest and most logical way to implement it?

I used several charts largely because I don’t know what I’m doing yet in Opto22, and because I wanted to seperate functions so that I can work on them independantly and possibly implement them in future patterns - sort of like designing user defined functions in PHP which can be recycled and used in future projects. If that makes sense?! LOL!

P.S. I can send you a zip of my current pattern/HMI if you’d like?


#16

Take a look at the timer expired command, it takes a float datatype in seconds (command help doesn’t tell you that).

You can create a down timer, put in the time interval you calculate, test for timer expired, fire the feed (Start On-Pulse??) and reset the timer, to do it all over again until your end time.

One way to learn about the things you can do in Opto is to read through the Command Help to get a feel for the built in commands. Many are self-explanatory based on their name, so it doesn’t take a lot of time.


#17

Thanks Philip! Toying with a few options here and I’ll definitely take a look at your recommendation as well.


#18

Hi tidan,

I emailed you some stuff and I love all of philip’s suggestions too.

Here are some links that might be helpful too. The fastest way to code your logic is to “leverage existing technology” and you’ve come to the right place for that. Lots of good sample code here on the OptoForums.

I already emailed you a timestamp subroutine, which is also found in the “Code Samples & Tips” section, a sticky at the top called: “Greatest Hits” (various Time/Date code near the bottom)
http://www.opto22.com/community/showthread.php?t=453

Also in that list, this more complicated scheduler:
http://www.opto22.com/site/downloads/dl_drilldown.aspx?aid=3961

Be sure to check out our “Best Practices” tech note, with lots of contributions from our awesome contributors here on the OptoForums: http://www.opto22.com/site/documents/doc_drilldown.aspx?aid=4375
One suggestion in that doc which might be helpful when you’re deciding on the various options or implementation details, is that you: “start with the HMI.”

Do you need to SHOW the various time feeding intervals? Or how long until the next one? I like philip’s suggestion of using a down-timer, especially if you want to show a countdown until the next feeding, which might be easier for a human to process than other options. I can picture a little kid wanting to know how long he/she has to wait to see the next feeding!

Happy coding!
-Mary


#19

Hi Mary,
Yes, I read that tip “start with the HMI” and will keep that in mind…currently I have done the opposite. haha.
For the numbers of feedings per feed period I’ll need the value to be configurable via HMI(which I almost had finished), and it would be nice to show the number of feedings that have elapsed and the countdown to the next feeding. I’ll work with the code sample you’ve done and see if I can implement Philips idea to achieve that.
Thanks!