Jump to content
Porter

VISA Lock Behavior

Recommended Posts

I am communicating with multiple instruments over a single serial port. These instruments are different but share a common communication protocol. Therefore, it makes sense to handle their data acquisition in separate loops. To do this, I open duplicate VISA sessions on the serial port (one for each loop). To avoid one loop interrupting the other's serial transaction, I am trying to use VISA locking:

- I would expect one loop to take control of the serial port while the others wait in some sort of FIFO queue (implemented within the VISA lock primitive).

- I would expect that if the timeout of the locks were set to 10 seconds, and there were 3 loops, each loop executing one serial transaction that takes less than 50ms, then the VISA lock should never timeout.

In reality, this is not the case. It is actually possible that one loop can starve the others by somehow holding onto the lock over multiple iterations (See figure 1). The graphs show the amount of time (in ms) spent waiting on the VISA lock per iteration of the loop. Clearly loop 2 never has to wait while loop 1 and 3 timeout after 10 seconds. Figure 2 shows a case where the loops are not starving each other but are clearly holding on to the lock over multiple iterations before releasing it.

My simulation VI is attached. COMM time is the time in ms allotted to each serial transaction. Poll period is the desired execution rate (in ms) of the loop. Starvation starts to occur when COMM time is equal to poll period. 

Note that I am not using "VISA Lock Async" because it is NOT re-entrant and is thus only appropriate if your system only needs to use one serial port at a time.  

I really didn't expect this kind of behavior. So my question is, does anyone know why VISA lock has been implemented this way?

Figure1.PNG

Figure2.PNG

VISA Lock Behavior.vi

Share this post


Link to post
Share on other sites

I don't know.

But for a similar case, in my ignorance and probably just crudely redoing what the lock is supposed to be for, I used semaphores named after the VISA resources. I acquire the semaphore immediately before every read or write, and release it then (forget the specific details of the readout):s.png

To deal with multiple serial ports I have a FGV containing the list of serial ports used and their associated semaphore references.

ResourceSemaphoreContainer.vi

 

Edited by ensegre
mistakenly attached password-protected VI
  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, ensegre said:

I don't know.

But for a similar case, in my ignorance and probably just crudely redoing what the lock is supposed to be for, I used semaphores named after the VISA resources. I acquire the semaphore immediately before every read or write, and release it then (forget the specific details of the readout):

To deal with multiple serial ports I have a FGV containing the list of serial ports used and their associated semaphore references.

ResourceSemaphoreContainer.vi

 

I do the same thing as ensegre.  I gave up on using VISA lock / unlock, because I keep forgetting the nuanced use cases like porter has shown (I'm getting old and my memory is going) so its easier for me to just use a semaphore and be done with it.

Share this post


Link to post
Share on other sites

Yup, when I have to do something similar I make sure there is only one VISA resource open, and one close.  Every read/write calls into an Action Engine that uses the VISA resource.  Sure you can get into cases where loop 2 is waiting to perform a write then read, because loop 1 is already waiting on the read to return, but in the end you really only have one port.  Data is going to come in and out for multiple devices and a lock of resources must happen at some level.  If you truly need 3 loops to run in parallel, and not block each other, then you need 3 VISA resources one for each piece of hardware.

Share this post


Link to post
Share on other sites

Unless you are in a situation where you can happily mix commands and replies, because each instrument identifies itself in the reply, and the response to a query may take a variable time, or parallelization would be advantageous, and the bus has some arbitration feature that prevents instruments from talking simultaneousy. Then you should create a readout queue, identify and dispatch messages received there, etc. It never occurred to me so far to do it, but that makes sense.

Share this post


Link to post
Share on other sites

I've implemented a workaround using names single element queues (essentially semaphores as ensegre suggested). So far it looks quite promising:

Figure 3.PNG

My simulation code looks like this now:

Figure4.PNG

I will post the source code one I've completed some tests this weekend.

Share this post


Link to post
Share on other sites

Why not Just put the Write/Read into a non Re-entrant VI (Action Engine) and have this called in your three threads?  Am I missing Something?  At the moment it looks like you are essentially doing what a non re-entrant sub-vi does for you..

Share this post


Link to post
Share on other sites
1 hour ago, CraigC said:

Why not Just put the Write/Read into a non Re-entrant VI (Action Engine) and have this called in your three threads?  Am I missing Something?  At the moment it looks like you are essentially doing what a non re-entrant sub-vi does for you..

You are absolutely correct but a non-reentrant VI is the equivalent of a blocking socket as opposed to an asynchronous one and we can argue all day about the pros and cons of that. (It usually ends up as "it depends")

Edited by ShaunR
  • Like 1

Share this post


Link to post
Share on other sites

Yeah I can see why you potentially would want to lock the Resource if it was being called asnychronously at run-time.  

I am just a little confused as it appears overly complicated unless there is some specific use case that I am not getting.  And historically I have found manually attempting to lock resources in this way is great but can lead to other headaches (Aborting the VI, and consideration of startup locked states etc)

Edited by CraigC

Share this post


Link to post
Share on other sites
3 hours ago, CraigC said:

Why not Just put the Write/Read into a non Re-entrant VI (Action Engine) and have this called in your three threads?  Am I missing Something?  At the moment it looks like you are essentially doing what a non re-entrant sub-vi does for you..

What if you have more than one comm port / instrument?  Are you going to make an action engine for each com port/instrument?  

Share this post


Link to post
Share on other sites

Nope, I tend to use templates or re-entrant drivers for numerous Resources.

By This I mean that he is trying to lock a single resource (Serial Port) and has multiple addressed instruments hanging off of some kind of bus.  Therefore you can still have a single "Core" Write Read VI which is non-rentrant rather than lock the Single resource he is using here.  I am no stranger to using multiple instruments but this is why I asked if I am missing something. So to re-iterate, It seems unnecessary if he is trying to lock a single resource.

And I also do this in a similar fashion when I need to lock multiple resources using the same driver.

I was merely trying to keep things simple if that is all that is needed.

Craig

 

 

temp.png

Edited by CraigC

Share this post


Link to post
Share on other sites

The reason why I am trying to do this is for code reuse, modularity and scalability. My system has multiple comm ports with multiple instruments on each comm port.

For example, a Modbus temperature controller and a Modbus PLC can be on the same comm port. I have 2 very different labview drivers for these instruments. Their init/shutdown routines are different. The polling period is different. The data types are different. I would rather not spend the time to develop a non-reentrant wrapper to go around the read-write functions of these drivers. Besides that, what happens when I decide to move the PLC to a different comm port? I don't want to have to modify the source code and re-compile. It should just be a matter of changing a line in a config file.

  • Like 1

Share this post


Link to post
Share on other sites
On 28/10/2016 at 0:46 AM, Porter said:

I am communicating with multiple instruments over a single serial port. These instruments are different but share a common communication protocol. Therefore, it makes sense to handle their data acquisition in separate loops. To do this, I open duplicate VISA sessions on the serial port (one for each loop). To avoid one loop interrupting the other's serial transaction, I am trying to use VISA locking:

Ahhh so I was missing something!!

Well it Looks like you need to lock the resources then.  I was trying to understand why you would do this on a single Resource.  However as you say if you need to lock it for expansion etc fire away.

Craig

Share this post


Link to post
Share on other sites

This is what I've come up with: VISA Lock Behavior Fixed.vi 

Dependency: Plasmionique Modbus Master

I tested it over the weekend on one of our systems. I apologize for forcing the mb_master package on you all but the locking/unlocking code (MB_VISA_Lock.lvlib) was absorbed into it. This is what I'm doing differently:

- Semaphore takes the name of the VISA resource from the Resource Name property instead of casting the session to string.

- Semaphore reference is stored in the User Data property of the VISA session. Just need to keep track of 1 wire for a VISA session.

- The Semaphore wraps the original VISA lock. That way we get the predictable scheduling feature of the semaphore but the safer locking features of the original VISA lock (supposedly it works over multiple LabVIEW application instances). 

Edited by Porter

Share this post


Link to post
Share on other sites

just curious why you used a SEQ for a "semaphore" vs an actual semaphore.  BTW I really like your version of the Modbus library.

 

  • Like 1

Share this post


Link to post
Share on other sites

Just a personal preference. I am more familiar with the behavior of SEQs and the error codes that they produce. I could have used an actual semaphore though.

Share this post


Link to post
Share on other sites

The Plasmionique library is very nice, I am advocating it internally as an example of good design :-)

When it comes to sharing of a communication channel (serial port, TCP-link etc) I've always attacked it by having channel handlers. For each channel I instantiate a channel handler of the correct type, and every need to access the channel (different device instances for example, or communication routed into the system from an external source) do so by communicating with the appropriate channel handler (normally using a SEQ). One upside to this centralized approach is that it is easy to see the whole picture of what is going on on a channel. The handlers normally run in the background, but they have their own user interface with communication stats etc available for debugging purposes, and/or they are DQMH-modules and all their actions are subscribable user events... :-) )

Edited by Mads
  • Like 1

Share this post


Link to post
Share on other sites

Thanks for the positive feedback :)

I used to implement a centralized queued state machine for managing each comm port. Perhaps my approach was flawed, but one reason why I stopped doing that was because I found myself having to gut existing instrument drivers to adapt them to my framework (because the VISA resource was in the handler and the logic was in the device instance).

By instead forcing VISA locks to behave (as I expected), I can just wrap the existing driver with my device instance. This also allows me to develop device code that is more portable (completely independent of my application's framework).

Share this post


Link to post
Share on other sites

That's true, if you have drivers that do not give you access to work with the generated messages, but requires you to allow them direct access to the shared resource, your solution makes sure you can still easily control the medium access. 

:)

In our channel handlers we also implement a device and/or channel reservation mode - which could solve the same challenge. The device wrapper would then request a channel reservation, prior to allowing the third party driver to access the channel in it smore direct manner...(We use the device reservation function when we need to ensure that no other entity is interfering with a complex operation towards one device on the channel). 

 

Edited by Mads
  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.