Porter Posted October 27, 2016 Report Share Posted October 27, 2016 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? VISA Lock Behavior.vi Quote Link to comment
ensegre Posted October 28, 2016 Report Share Posted October 28, 2016 (edited) 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 Edited October 30, 2016 by ensegre mistakenly attached password-protected VI 1 Quote Link to comment
bbean Posted October 28, 2016 Report Share Posted October 28, 2016 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. Quote Link to comment
hooovahh Posted October 28, 2016 Report Share Posted October 28, 2016 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. Quote Link to comment
ensegre Posted October 28, 2016 Report Share Posted October 28, 2016 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. Quote Link to comment
Porter Posted October 28, 2016 Author Report Share Posted October 28, 2016 I've implemented a workaround using names single element queues (essentially semaphores as ensegre suggested). So far it looks quite promising: My simulation code looks like this now: I will post the source code one I've completed some tests this weekend. Quote Link to comment
CraigC Posted October 31, 2016 Report Share Posted October 31, 2016 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.. Quote Link to comment
ShaunR Posted October 31, 2016 Report Share Posted October 31, 2016 (edited) 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 October 31, 2016 by ShaunR 1 Quote Link to comment
CraigC Posted October 31, 2016 Report Share Posted October 31, 2016 (edited) 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 October 31, 2016 by CraigC Quote Link to comment
bbean Posted October 31, 2016 Report Share Posted October 31, 2016 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? Quote Link to comment
CraigC Posted October 31, 2016 Report Share Posted October 31, 2016 (edited) 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 Edited October 31, 2016 by CraigC Quote Link to comment
Porter Posted October 31, 2016 Author Report Share Posted October 31, 2016 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. 1 Quote Link to comment
CraigC Posted October 31, 2016 Report Share Posted October 31, 2016 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 Quote Link to comment
Porter Posted October 31, 2016 Author Report Share Posted October 31, 2016 (edited) 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 November 3, 2016 by Porter Quote Link to comment
bbean Posted October 31, 2016 Report Share Posted October 31, 2016 just curious why you used a SEQ for a "semaphore" vs an actual semaphore. BTW I really like your version of the Modbus library. 1 Quote Link to comment
Porter Posted October 31, 2016 Author Report Share Posted October 31, 2016 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. Quote Link to comment
Mads Posted November 22, 2016 Report Share Posted November 22, 2016 (edited) 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 November 22, 2016 by Mads 1 Quote Link to comment
Porter Posted November 23, 2016 Author Report Share Posted November 23, 2016 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). Quote Link to comment
Mads Posted November 23, 2016 Report Share Posted November 23, 2016 (edited) 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 November 23, 2016 by Mads 1 Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.