
strantor2 (Customer) asked a question.
I've requested more powerful search/replace functionality and been promised there is something coming down the pipes eventually, but I need it now, so I wrote it myself. Thought I would share with anyone else who is missing this ability (or maybe not knowing they're missing it), to get by until the official solution is rolled out.
What am I talking about? Let's say you're writing a conveyor program and you make a UDS that gets re-used for each conveyor, so you make an array of the UDS and write a program for your first conveyor that looks like this:
All those Conveyor(1).something.somethingElse.anotherThing(4) are great, love the UDS structures, but once I'm done writing the program for Conveyor #1, I'd like to copy it, and change all the Conveyor(1).something.(...).(...) to Conveyor(2).something.(...).(...). PSuite's search/replace function doesn't let me do that.
It only lets me if I drill the search criteria all the way down to the very last level of the UDS:
While marginally faster than manually changing the address in every single ladder instruction one at a time, it's still painfully slow having to do a separate search/replace operation for each address, especially since the "replace with" field goes blank every time you do a new search in the "Find Tag Name" box and you have to re-type the whole conveyor(2).Logic.something.something each time.
So I wrote this python script which does a search/replace at the top level (or whatever level you want) of the UDS (or just an array, 1D, 2D, Whatever) outside of PSuite. To use it you'll need to download & install the latest version of Python 3 (25mb), and run 'pip install tabulate'.
From there you can run my 'launchToolkit' script. Open it in Notepad first and modify it to have the path to your PSuite project and the desired "searchFor" and "replaceWith" text and run it. It's all command-line, no fancy GUI (sorry) but it does the job. Here's how it looks:
Doesn't matter what you enter here, the only function for now is array/UDS find/replace. More functionality to be (maybe) added later if I have time.
I press 1, enter:
From this screen I can choose which tasks I want to run search/replace in. I could run it on all the tasks by entering A, or all but a couple by entering E and the specifying which ones I want to exclude, but since I made of copy of my Conveyor1 task and named it Conveyor2 (but it still contains all Conveyor(1).x.y.z) I will enter I, and specify to include only #3, which is my Conveyor2 task.
Once I do this, the script will delete everything off the list of tasks except the one task I specified to include, present the list to me again (now containing only the one task) and I confirm by pressing A, enter
Now I'm shown a list of all the Conveyor(1).x.y.z tags in the Conveyor2 task, and in the same fashion as above I can choose replace all 75 of them, or all but a handful, or just a couple.
I choose ALL, so enter A, enter.
If I had chosen to run search/replace on multiple task, then the next task would pop up at this point, but Since I only ran the search/replace on the one task, and that task is complete, we are now at the end and it asks whether to save back to the original file or create a new one. I save to the original (NOTE Don't have the project open in Psuite if you do this).
I choose ALL, so enter A, enter.
If I had chosen to run search/replace on multiple task, then the next task would pop up at this point, but Since I only ran the search/replace on the one task, and that task is complete, we are now at the end and it asks whether to save back to the original file or create a new one. I save to the original (NOTE Don't have the project open in Psuite if you do this).
Oh yeah, almost forgot to cover my backside. Make a backup of of your project before you run this, I'm not responsible for any loss of data. Use at your own risk (not that it's risky, but I feel like I'm supposed to say that). Also I'm not going to offer any meaningful tech support on this or entertain feature requests. I made it for my own use and if anyone else gets use out of it too, that's great, but I'm not going to be spending hours of my days helping others getting their Python installed. That's what Google is for. If you have questions, feel free to ask, but don't take it personally if you still don't have an answer after a few days.
Thanks for sharing. It looks interesting. I like reading over python scripts and seeing how other people do things.
Sure, feel free to browse, but whatever you do, don't try and learn anything from my Python programming. It's a textual SEMA build at best.
Thanks for sharing what looks like a very useful tool. I do wonder though, if indirect addressing couldn't have saved you a bunch of time in this case. If in you subprocedure instead of using Conveyor(1).something you could have used Conveyor(index).something. You could call the subprocedure in a for loop and incremented index, or just called the subprocedure directly over and over and increased or set index before calling it.
I do this already as a half-measure while waiting on the eventual "user-programmed function blocks." I copy the entire UDS (EX: Conveyor(3)) or more often just an element of it (Conveyor(3).sensors) to another tag of the same UDS ("ConveyorToEvaluate") or ("ConveyorToEvaluate.sensors"), then call a "run when called" task that processes "ConveyorToEvaluate" and then copy ConveyorToEvaluate back to Conveyor(3). Then put that inside a For loop, so it happens to all conveyors 1-20.
But it has some drawbacks. For one, timers don't function predictably inside a for loop (or inside a run when called task?) - not sure; when I do what I described above, with timers, they're out to lunch. And you can't see what's ongoing in the logic (unless you're just really curious about conveyor(20)), which is one of my main reasons for using a PLC in the first place. Sure you can see the bits and values in Data View but you can't see the pretty pictures that ladder logic paints.
So I found myself using the for-looped run when called tasks, passing bits out to timers that run every scan and passing back in, and breaking out important bits of ladder that need to be visible for each section, and every time I decide a new timer is needed, I have to go add a bunch of them and break more rungs out of my run when called task, until I'm in some awkward position where half my logic is inside the running when called task and half outside, it's confusing even for me and I wrote it, and what was supposed to save time wasted time. I came to the decision that it would just be worlds better to have a separate run-every-scan ladder file for each conveyor section, and be able to copy it.
If I could get user function blocks in productivity I would... well, I'd be really really happy and buy even more ADC stuff (wink-wink)