Recipes in DAQFactory part 2


Recommended Posts

In a previous post I talked about how to do one of the two main types of "recipes". In that case a recipe was a set of preset values that would be sent to a device. This might be used, for example, when mixing paints. A recipe would be the amount of the primary colors to make the final result.

In this post I'd like to show you how to do the other type of recipe. In this case, a recipe is really just a high level set of steps to perform. Truthfully, DAQFactory scripting is just low level, highly flexible, recipe system of this sort, but with power comes some learning, and you may only want to give you end users a few choices. My description is a little detailed, but the sample is pretty easy to expand on without completely understanding it all, and truthfully, the sample shows some good ways to make many tasks easier, so is worth figuring out if you are going to do any advanced DAQFactory scripting.

In the attached sample (requires 5.78+), I have created a simple heater / temperature simulation that can be controlled from a simple, user definable recipe. The user can create up to five steps in their recipe and they have a few choices for what they can do in each step, mainly, turn the heater on, turn the heater off, wait for the temperature to reach a certain point, and do a pause. The user can specify the recipe by selecting from these options in combo boxes, with edit boxes that appear when a parameter is required.

As usual, I designed this sample so you can easily build on it. The possible recipe steps are stored in three arrays, one for the name of the step, one for the action and one for a flag that marks if there is a parameter for the action. These are all defined in StartUp. To add more steps possibilities, just add on to the array in StartUp. Note that the StepAction is a string that contains a DAQFactory script line. It is injected with a value using format() which is what allows a parameter to be set. Just use %f in the line wherever you want the parameter.

The recipe itself is stored in two arrays, one for the step index, and one for the parameter (if any for that step). At the end of startup, I filled all the combos with the choices. I only made 5 combos, but you could easily make the recipes longer by adding more combos and edit boxes. Just change MAXSTEPS and add the new screen components, remembering to edit the parameters for each.

Speaking of the screen components, the recipe gets specified by the user using a set of combo's and edit boxes. The edit boxes appear and disappear depending on whether the selected step has a parameter to specify. This is done in the OnPaint event of the edit box (you have to View - Events to see it). Yes, I know I've previously lectured on why you shouldn't use edit boxes on main screens, but presumably you would NOT put a recipe specification on a main screen, and instead would create a popup or secondary screen that has to be acknowledged.

Finally, the actual recipe gets run in the RunRecipe sequence, which really just loops through each step and calls execute() on it. Notice how, instead of using delay() for the pause step, I created a sequence function. This allows the recipe to be stopped midway in a controlled manner. This is better than simply stopping the RunRecipe sequence because if we do endseq() on a sequence, we don't know where it ended.

So, to try it, I recommend creating a recipe something like this:

Heater On

WaitForTemperature xx.xxx

Heater Off

where xx.xxx is some temperature that is about 0.5 higher than the current displayed temperature. Then run the recipe.

Feel free to post with questions.

Again, this sample requires DAQFactory 5.78 or later.

recipesample2.ctl

Link to comment
Share on other sites

  • 8 months later...

Hi,

This is a real good piece of information. I guess you can ignore my post from sequences and scripting (switch, case statements), unless you have some time and it is easy to show. I would like to be able to upload my current temp control code into your recipee code. For the future, I figure I will vary recipees for example by only adding a few lines of code such as a digital output line that will trigger a cooling system in my temperature reactor.

Link to comment
Share on other sites

Well, if you just want to delay for 3 hours without control, there is already a step for that. Otherwise if you want control, you'll have to add another step. I'd duplicate the delayfor() sequence, then modify it and add some control script. You'd then add a new possible step in the startup sequence, it will be [6] and might look like:

StepName[6] = "Hold"

StepAction[6] = "HoldFor(%f)" // %f means put a floating point value here, this is the parameter.

StepParameter[6] = 1

The HoldFor sequence might look like:

function HoldFor(len)

   // capture current temp and hold at that temp:
   private holdtemp = temperaturein[0]
   // hysteresis on controller
   private hyster = 3 

   private endtime = systime() + len
   while ((systime() < endtime) && RecipeRunning)
	  // control
	  if (temperatureIn[0] > holdtemp + hyster)
		 heater = 0
	  endif
	  if (temperatureIn[0] < holdtemp - hyster)
		 heater = 1
	  endif
	  // always delay in loop:
	  delay(1)
   endwhile

Link to comment
Share on other sites

Hi again,

I have tried the code and I am getting hung up when I run the recipee. I have Daq Express and have virtually no access to create many components. I don't know if this is my problem. Anyway, I added a sequence called control and wrote the code as you had it, starting with:

function HoldFor(len)

I also edited startup and added maxsteps as 6 now. I had to edit your combo boxes and added an option for Hold. It was number 6 in order and F.

That is all I did.

When I run the recipee I do for example:

Heater on

Waitfortemp 90

heater off

waitfortemp 60

hold 60 ( I don't know if this is right.....at this point I want it to hold the 60....I think the parameter entry is in seconds and is the amount I want to hold it for???)

Either way, when it gets to hold 60 it just ends the recipee.

I also get an error: Ch or fxn not found: Line 1: RunRecipe Line 7 - Uncaught error in sequence RunRecipe

(the line is: execute[format(StepAction[stepnum], RecipeParams[RecipeStepCount]))

There is also an error in the control code: the line: private endtime=systime() + len (what is len...is it length? and do I have to specify it as anything?)

I am relatively new with this application and I couldn't find many guides on recipees. I am working on a project for school and I have to create a GUI kind of like the recipee and eventually I have to add more steps.

For example:

If I want

heater on

waitfortemp90

heateroff

waitfortemp60

hold (for three or whatever hours at 60)

maybe a rapidcooling output here

endrecipee

As you can see that is more options than your recipee. To make more combo boxes, do I need a newer version of daqfactory such as starter???? or can I just copy and paste the components.

Thanks

Link to comment
Share on other sites

  • 4 years later...

Hi there,

We are applying a recipe approach to control and synchronize several instruments, based loosely on the control file given in this thread. I am getting snagged on one step, and feel like I'm missing something obvious. We have defined a step where we want to 'delay' the continuation of the recipe until a data file is written to a directory. An instrument (the ACSM) takes a slightly variable amount of time to complete a run, but writes a data file based on the time (at UTC) at which the run is completed. So we just want to scan its data directory for when that file is written and then resume the recipe (following steps) when that's done.

I have written a sequence (pasted below) that polls the directory and seems to work, however if I implement it as a sequence (using beginseq) it just runs in parallel with the 'run recipe' sequence and doesn't cause anything to actually pause. On the other hand, if I implement it as a function (as the recipe steps are, and as is shown below), then DAQFactory hangs until the file is actually written (at least the interface does, I'm not sure if acquisition and control are affected).

I have tried changing the thread priority and that doesn't help. I've also played around with making a channel that polls the directory, but didn't get any traction.

This seems like it should be a straightforward thing to do - what am I missing?

many thanks,

Andy

My sequence/function:

function ACSM_check()

private string temp_path

private string temp_date

private string temp_filename

private string ACSM_path = "z:\ACSMData\ScanData\"

private utc_time

private time_lag = 5 //to allow for time lag in writing/accessing file

private ACSM_done = 0

if(file.getfileexists(ACSM_Path+"\*") == 0)

throw("Can't find ACSM Directory Specified!")

endif

while(ACSM_done == 0)

utc_time = systime()+4*3600 - time_lag

temp_date = FormatDateTime("%Y%m%d",utc_time)

temp_filename = temp_date+"_"+FormatDateTime("%H_%M_%S",utc_time)+".itx"

temp_path = ACSM_path+temp_date+"\"+temp_filename

ACSM_done = file.GetFileExists(temp_path) //flag for whether ACSM file has been written this second

delay(0.2)

endwhile

Link to comment
Share on other sites

If you call it as a function from the user interface thread (meaning from the command / alert window, or in a component Action), then yes, it will hang the user interface until complete. However, presumably you are running the recipe in its own sequence? If you start that sequence using beginseq() or similar and not as a function, then it will run as its own thread. Then if it calls this sequence as a function, the function will run in that sequence's thread and not hang the UI.

Note that sequence priority has no effect if you call the sequence as a function.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.