Here you go Desert Dog.
To test the code, put it in one of your projects and turn on Symbolic Addressing Priority.
Method 1 calculates the pointers only one time upon startup. This saves quite a bit of scan time later on, since you don’t have to calculate them every time you want to copy a recipe. This is actually my preferred method, because I can change the structure of my DBs, without changing any code whatsoever (to see this, go ahead and add some more values in the Recipe UDT, recompile the blocks, download them, and restart). To test it, stick FC3 in your startup block. It will then find the start pointers and recipe lengths and store them right at the beginning of the recipe block. Note that I find the length in bits, bytes, and words whether I need to use them later or not. Also, I use dummy values if I don’t actually need the output variable. I could have created a new function instead, but since it is called only on startup, I don’t really care about scan time efficiencies in this case.
After the PLC starts and you go online with DB5, you’ll see that many of the pointers you need for dynamic indirect addressing are right there. Looking at FC5, if I force “DB Recipes.RecipeSelect” to any value above zero, it will use these values to create a pointer to the correct recipe, and then call FC6. In FC6, we just use these pointers to format AR1 and AR2, and grab the recipe length in words for the loop counter. A couple of notes: Obviously, if you enter a value greater than the number of recipes in the block, this code will blow up, but this maximum number can be calculated during startup as well. Also, I used DB5 for all of the data, but you can easily use different blocks for the Current Recipe and the Stored Recipes. If you do that, just use an instance DB for one of the blocks. All in all, this method is very scan time efficient as well as very generic. I used names like “Recipe” in FC4 for clarity, but in reality I have a library function that uses generic terms like “InputData”, and I use it whether I’m finding pointers for a conveyor or a VS Drive.
Method 2 is closer to what you were doing. It is still generic, but it has to calculate the pointers every time a recipe is copied. If you test this, make sure you remove FC3 from the startup block first. In FC7, if you force a value in the RecipeSelect, it passes the pointer for Recipe[0], the CurrentRecipe, and the SelectedRecipe. That’s all the info we need to determine the recipe length and offset. Again, go ahead and insert new recipe variables in the Recipe UDT, and you still won’t have to change a line of code. You can even insert stuff right at the beginning of the block, and it won’t matter.