Variable I/O unit configuration

Hi…we have been using Opto22 in X-ray equipment we produce for mining companies since about 2000. The equipment uses similar, but not exactly the same I/O configurations for each installation, so now we have 50 or so controllers running various flavors of a similar strategy; now we want to standardize the code.

I am able to provide some level of abstraction by using pointer tables that are loaded at powerup. And I can have site-specific charts that get called (or not) for specific site requirements which can all be rolled into one monolithic strategy. All of our equipment uses PAC-R1 controllers (and for those legacy Ultimate I/O systems, we’ll require the customer to upgrade). The first 7 modules on the rack are exactly the same in all equipment and configured the same. Modules 8-12 or 8-16 depend on the site requirements and sometimes there are additional I/O racks.

The problem I’m having is how to include unique controller configurations in one deployable strategy. My thought is to use a subroutine to turn a point on/off based on point address, which of course has to examine the module and determine if it’s high density or standard 4-channel I/O etc. The addresses would be loaded into a table via a configurable strategy download file. This works fine if the I/O unit and controller are the same, but gets very cumbersome when there is a second or third PAC-R1 being used as an I/O unit. I have written routines to perform generic MMP read/writes given an IP address, but this gets very cumbersome. I’m looking for thoughts on how to handle this. Ideally, I’d love to create I/O units and points dynamically within the strategy, but I don’t think that’s possible at least not without Forth programming.

Hi daveslc,

Good question. A few thoughts come to mind. When you mention that second or third PAC used as an I/O unit – could you include those in the same “standard” strategy, just have them disabled and only enable them if/when appropriate?

Another thought: you could do something a little less clunky than the MMP read/writes by using commands like: “Move I/O Unit to Numeric Table” – but the downside to this (and the MMP method) is you lose the ival/xval distinction.

A trick I’ll throw out which might help reduce the amount of differences you have between your smaller (just one rack?) and bigger systems. Let’s say those modules 8-12 are usually unused. Even though they are physically on the same rack as modules 0 - 7, you could configure them logically on a second rack. Just use the loopback address ( for one and the R1’s IP address for the other. Then you could enable that “second” rack only for the bigger systems.

I hope that makes sense/helps!


Hi Mary,

Thanks for the reply. The “Move I/O Unit to Numeric Table” looked like a good possibility (creating one 1536 element table and reading up to 3 enabled I/O units into this table) until I read that for points which are not configured, the value reported is 0.0. I’m assuming this means points not configured in PAC Control, as opposed to being configured in PAC Manager. Having to define the points in PAC Control is a problem since the I/O units vary as to which points are defined, where they are located, module types (high/low density, etc). If it could see the points configured in PAC Manager (i.e. the I/O side of the brain), this would work.

Maybe someday we’ll have something like my_pointer = &CreatePoint(point_type, module_number, point_number);

Thanks again,

Forgive me, I was thinking of the legacy commands like “Set HDD Module from MOMO Masks” – but these only work for your high-density digital commands. (They’re “legacy” because originally we did not provide the ability in PAC Control to configure the HDD module and points, so you could not use normal I/O commands, and you didn’t get i/xvals like you do when a “real” point is configured.)

A couple other thoughts: could you break your “standard strategy” into smaller pieces, perhaps have “standard charts”? Are you using subroutines at all? That way you could leverage the fact that importing a chart brings the relevant variables, I/O Units, etc. and subroutines could let you write some generic, re-usable code. PAC Control is designed to be used that way. But it’s always fun to brainstorm creative ways to get outside the box! :slight_smile:

Sorry I didn’t see this sooner, I ran into a pretty similar situation recently. The products I test can widely differ in their arrangement and number of relays that I need to monitor. The way I decided to get around having to re-write the program each time we get a new model was to lock in a naming scheme for the points and move them to the appropriate pointer tables based on the names using the “GetPointerFromName” command.

So in my control strategy, the points are named something like C01_240Input_R01_00_00. The C01 refers to the sample number (I typically test up to 12 identical samples at once). The 240Input tells me what kind of I/O point it is. R01 tells me which rack it is. The first set of 00 tells me which module on the rack, and the second set of 00 tells me which point on the module. Then I index through each possible point configuration and build a string based upon the current indices and use the GetPointerFromName to attempt to grab the I/O point. If it is null, it just returns and indexes to the next point. If it is a valid pointer, then I move it to the pointer table for that particular sample on test. In some samples element 2 may be a widget while in other cases it’s a wocket, so then all that’s left is for me to put the correct names into a String table with the same number of elements as the pointer table.

The chart that sets the configuration up is called on the startup of the Display program, but after that it shouldn’t need called again for the duration of my testing (3+ months at a time). Things to be wary of, depending upon your hardware it could take a while to index through and move all the correct points to the right pointer tables. So filtering in the index routine should be used wherever possible, for example if you know the first 8 modules will NEVER change, then you might as well leave those indices out of the loop. Or if you know you aren’t using an HD modules, only index to 4 points per module instead of 8 or 16 or 32. For reference, it takes maybe 10 seconds to index through the ~300 points in my setup, so it’s not debilitating or anything - it’d just be a nuisance to have to wait that long if I had to re-initialize it multiple times per day.

I’m almost certain your implementation will be vastly different from mine, but I figured I’d share my method to attack our common issue to hopefully inspire you to your solution!


Thanks Dusten, for the thorough explanation. And we have taken similar tacks in regard to naming conventions for our points, and I used to use GetPointerByName() quite often, but now I load the pointer tables at startup and generally access the point from a table element. This provides one level of abstraction that does make the code somewhat more universal. However, the real problem lies in the fact that the PAC Control code needs to be compiled with it’s controller I/O configuration. And because the controllers are configured differently (HD versus standard DO modules, varying number of modules etc), that means even if I use a standard naming convention, I still have to have separate projects for every installation rather than one deployable project that could go to all controllers. If only the configuration could be linked into the code or PAC Control would offer some way to create the points on the fly.

Hey all you GetPointerFromName() fans, I just uploaded a couple little examples you might like to:

  1. Get a list of tag names (from your idb.txt file)
  2. Beef up the related Get[B]Value[/B]FromName() with a little nice formatting, uses related Get[B]Type[/B]FromName()

Happy OptoScripting!

It’s too bad you are using the R1 instead of an S1, the solution is super simple if you use an S1 even though there is a cost penalty, it’s not much. You would just provide multiple brain configurations (each has to have different IP addresses) one for each scenario. Then based on a integer model number in the S1, you just enable the EB1 configuration. All the Brain configurations would be part of the S1 strategy with all Brains disabled and then just enable the brain that has the correct config.
Mary, this brings up a question, since you can’t address the R1 brain different from the R1 controller side, it would be nice to allow multiple configurations on the same loop back address and only allow one to be enabled at anytime. Each config would have to have a unique name.

I like this idea! Actually, there is a way to have two configs right now – use the loopback address for one and the actual IP address for the other, as I mentioned it reply #2 above.

If you daveslc could change the IP address in his I/O Unit’s configuration on-the-fly (I’ve heard other requests for that), that could help. I’ll make sure there’s a request in for that.

You CAN programmatically make a particular brain’s IP change to match the (fixed) I/O Unit configured in your strategy. But I’m not sure that would help for this situation. Check out this post complete with a Really Corny Joke in it for details.