I’m starting out writing my first PAC Control strategy and had a question about how best to handle communicating with multiple serial devices on the same comm handle.
For reference, I’m talking to a bunch of Alicat devices through one of their BB-9 boards, which essentially combines separate devices onto a single RS-232 connection, each device being addressable by a unit ID (“A”, “B”, etc).
All I really want to do is be able to poll the device status and occasionally send a command or two. I know how to do this using a single chart for a single device, but with multiple devices operating on a single comm handle, it becomes a bit more complicated.
My initial thought was to have separate charts for each device and use a flag lock to ensure they don’t overwrite/overread each other on the comm handle, with a delay at the end of each. That does seem to work, but it also seems like the charts aren’t necessarily executed in order – sometimes my B device will get data for a while (10-20s) before A or C get anything.
I was wondering if there is a better way of doing this (besides building my own round-robin queue) to ensure each chart gets run in order, or if there is a different way of doing it altogether.
It is difficult to manage a singled comm handle in multiple charts, as a rule you should avoid that, though as you found it can be done with appropriate locking.
I would recommend a single chart in a loop where you iterate through the Unit Ids. You can also implement a timer/timer table for polling each device. That is useful for when you may have devices that are offline where you can increase the polling time for those to keep the rest of the devices responsive.
Here is a short example of a polling timer table:
if(utPollingUpTimer == 0.0) then
StartTimer(utPollingUpTimer);
endif
if(utPollingUpTimer > 0.5) then
//increment timer table
fPollingTimerValue = GetRestartTimer(utPollingUpTimer);
for nLoopVar = 0 to 10 step 1
ftTimerTable[nLoopVar] = ftTimerTable[nLoopVar] + fPollingTimerValue;
next
endif
Here is a structure on how to handle polling the devices in a loop with handling a timeout and increasing the poll time in that case. DoCommWork could be a subroutine you create or you can put the logic directly in the loop.
for nLoopVar = 0 to 10 step 1
if(ftTimerTable[nLoopVar] > ftPollTime[nLoopVar]) then //time to get data
ftTimerTable[nLoopVar] = 0.0;
sUnitId = stUnitId[nLoopVar];
//Do comm work here for sUnitId
//DoCommWork(sUnitId, sResults, nTimeoutError);
if(nTimeoutError) then
ftPollTime[nLoopVar] = 60.0; //Retry in 60 seconds
else
ftPollTime[nLoopVar] = fDefaultPollTime;
endif
endif
next
For sending commands (based on some other trigger), I would create a queue to put the commands in and check the queue as part of the loop. The command queue could be outside of the polling timer condition so they are sent immediately.
I have a follow-up since the first answer ended up working out so well – “no good deed goes unpunished”, after all
Aside from the Alicat devices, I’m also working with several different Bronkhorst devices which use different RS-232 channels, so different comm handles. The devices are, for my purposes, the same, so I’d like to run the same chart loop on each of them, but I recently discovered that (it seems) comm handle variables are read-only: I tried to set up a script that would swap a “chCurrentDevice” variable with the actual comm handle so I could loop through each one, and reuse the various device communication code within that loop, but get an optoscript syntax error at the point of assignment. So that approach seems impossible now.
What’s the recommended way to have a common control flow across several different comm channels? I know I could just duplicate the chart and change variables, but that’s not very DRY, and as the code becomes more complex it would be a maintainability nightmare.