12-08-2017 11:16 AM
@550nm wrote:
Hi clmartinez,
It seems "CL Read CCD Area" fills-in an array - is that right? If so, the "Data" array must be preallocated. If the calling VI isn't allocating the Data array, then I'm guessing this could be a problem. It would be easy to modify "CL Read CCD Area" to preallocate the array-space, but I don't know how big the array should be. Do you know how many pixels are expected and how many bytes-per-pixel? By the way, I didn't see an example program in the.zip file - can you attach it?
HI There,
Thanks for the help! Yes, the array will be 8 scans x 2048 pixels, not sure how many bytes per pixel but it's at least a 14 or 15 bit precision (2^15). Attached is the Example.vi. Thanks!
12-09-2017 07:31 PM - edited 12-09-2017 07:34 PM
I made some changes to clean-up the code and fix a pretty serious flaw in the logic.
It turns out the Data array was being allocated in the "Example Program" after-all, however, the "CL Read CCD Area.vi" was doing something odd/illogical. It was acquiring the Data array, then waiting for the Data array to be ready!? See comments in the CL Read CCD Area.vi included in attached LLB.
Considering all the changes, there is quite possibly some new bug(s) in addition to other pre-existing bugs. I think there is a CL camera I can use at work, and intend to debug this code Monday evening.
12-10-2017 06:02 PM
The original library definitely was flawed. While it most likely usually would work to pass in an array like this into a Call Library Node to be used in the DLL later on after the Call Library Node returned control to LabVIEW, it is absolutely a disaster waiting to happen. The array is usually kept alive (and not getting relocated to another memory area if wired in the calling diagram in the correct way, but this is the crux of this whole thing. Just add an array manipulation function anywhere on the diagram for this array and be not very very careful to make sure that function can never be called before the Ready polling returns with a true status and everything goes awry. LabVIEW is a managed environment and reserves the right to reallocate memory at anytime. That it doesn't usually do so because that is in most cases counterproductive for the performance, does not mean that it never can happen. An array passed into a Call Library Node is only guaranteed to be valid and non-relocatable during the Call Library Node call itself. After it returns there are just habits of LabVIEW how it handles those arrays, first dictated by making sure code executes right, then by not creating unnecessary performance killers and after that just whatever is easiest to implement for the LabVIEW developers. These variables can change between LabVIEW versions due to new optimizing algorithms being introduced and other necessary code changes for other features. So relieing on a certain non-guaranteed behaviour of LabVIEW is really just creating a time bomb.
Two things about your library:
1) You should configure the pointer you create correctly as pointer sized integer in all Call Library Nodes and as 64 bit integer in the LabVIEW diagram. Otherwise those VIs will quickly bomb when someone tries to use them in a 64 bit version of LabVIEW. Only reason to keep them as 32 bit integer is if you absolutely have to maintain compatibility with pre LabVIEW 2009 versions as the 64 bit version was introduced then.
2) I would wire the array through in the abort case of the Read CCD Area VI and return an extra boolean that indicates if there was a timeout. It shouldn't be the VIs decision to disrupt the passed through array buffer but leave that up to the caller. The alternative would be to have the VI allocate the necessary array on every call if it was successful. As it is now it is way to easy for an inexperienced programmer to create an array pointer that contains for instance 4096 bytes and an array that contains a bigger number, resulting in a bufferoverrun read, which is not as bad as a bufferoverrun write, but still has the potential to crash the program if the area after the end of the buffer happens to fall outside of any valid allcoated heap space for the current process.
12-11-2017 11:49 PM
@rolfk wrote:
[snip].
Two things about your library:
1) You should configure the pointer you create correctly as pointer sized integer in all Call Library Nodes and as 64 bit integer in the LabVIEW diagram. Otherwise those VIs will quickly bomb when someone tries to use them in a 64 bit version of LabVIEW. Only reason to keep them as 32 bit integer is if you absolutely have to maintain compatibility with pre LabVIEW 2009 versions as the 64 bit version was introduced then.
2) I would wire the array through in the abort case of the Read CCD Area VI and return an extra boolean that indicates if there was a timeout. It shouldn't be the VIs decision to disrupt the passed through array buffer but leave that up to the caller. The alternative would be to have the VI allocate the necessary array on every call if it was successful. As it is now it is way to easy for an inexperienced programmer to create an array pointer that contains for instance 4096 bytes and an array that contains a bigger number, resulting in a bufferoverrun read, which is not as bad as a bufferoverrun write, but still has the potential to crash the program if the area after the end of the buffer happens to fall outside of any valid allcoated heap space for the current process.
Thanks for the tip re: pointer allocation - will make it a rule from now on.
Re: 2,
I _had_ the pointer and array allocation on the (new) CL Read CCD AreaX" diagram - but then where to _dispose_ the pointer? It seemed "asymmetric" to allocate the pointer in the Read VI subVI, and de-allocate in Main/caller, so pointer allocation was moved up, into Main, with the de-allocation. That resulted in pointer allocation in Main, but array allocation in subVI - talk about an opportunity for memory-space discrepancies... So both array and pointer allocation is in Main - leaving CL Read CCD Area much more like it was originally. Also notice that the Data-in is U8, while the Data-out is U16. This allows the two memory allocations - pointer and array - to be clearly identical - at the cost of a cast in CL Read CCD Area.
It is right for CL Read CCD Area to not return any Data if none is acquired.
If I spend any more time on this, I might implement yet another alternative which is to make the CL Read CCD Area a functional global with Init, Close and Read functions - so to keep the memory manipulations safely tucked-away where they belong, while allowing the allocate and de-allocate to occur in the same "scope".
12-12-2017 03:10 AM
The pointer definitely needs to be somehow managed by the caller. It's nasty and anti-LabVIEW like but sometimes almost unavoidable. The alternative would be to rewrite the DLL to not rely on a buffer being passed into it to be used asynchronously while the function already has returned back to LabVIEW. Dataflow and asynchronous data operation do not go easily together.
An alternative might be to rewrite the whole LabVIEW library to be a class, make the pointer a Data Reference Value in the private data of the class and make sure to allocate at least the DVR if not the pointer inside itself at the "Creation" of the class. Most likely it would even work putting the LabVIEW array in the DVR but that feels brittle. The only guarantee that that array doesn't get reallocated between calls is the fact that it would be performance wise not a good idea to do so for the LabVIEW compiler. But LabVIEW never made hard guarantees about data buffers not being relocatable beyond the duration of Call Library Node calls. The fact that they normally stay put in memory where they were created is a mere performance reason as relocating memory costs time and CPU resources. With a class and a DVR the pointer would be hidden from the user of your library so that it feels more LabVIEW like.
As to your read function returning an empty array, yes it is a valid argument. But depending on the array size I tend to not unnecessarily resize arrays as they are passed through functions even if there is an error and indicate the error sitution in the error cluster or a separate status output. For the array in this case it doesn't really make much of a difference.
All in all the DLL is clearly written by a C programmer to be used in a C program. The idea of passing in a pointer in to be used asynchronously doesn't go very easily in all modern managed programming languages, including .Net and others.
12-12-2017 06:28 PM - edited 12-12-2017 06:42 PM
@clmartinez100 wrote:
@550nm wrote:
Hi clmartinez,
It seems "CL Read CCD Area" fills-in an array - is that right? If so, the "Data" array must be preallocated. If the calling VI isn't allocating the Data array, then I'm guessing this could be a problem. It would be easy to modify "CL Read CCD Area" to preallocate the array-space, but I don't know how big the array should be. Do you know how many pixels are expected and how many bytes-per-pixel? By the way, I didn't see an example program in the.zip file - can you attach it?
HI There,
Thanks for the help! Yes, the array will be 8 scans x 2048 pixels, not sure how many bytes per pixel but it's at least a 14 or 15 bit precision (2^15). Attached is the Example.vi. Thanks!
Well, my CL camera is the HSUSB variety, so I had to call the CL Open HSUSB Camera.vi - which also returns a camera "handle". Besides that, nothing changed and the "Example ProgramX.vi" seemed to work (at least some pixel data was updating and LabVIEW didn't crash.) Caveat: my camera has 256pixels, so, only the first 256 U16 data-values were changing for me.
This VI/llb includes the HSUSB/Serial toggle, but also triggers the Exposure-time change event so that the initial GUI settings are applied prior to "Acquire".
By the way, be sure clcamiface3.dll is in the same directory as this .llb (or Example Program.vi).
12-12-2017 06:40 PM - edited 12-12-2017 06:42 PM
@rolfk wrote:
The pointer definitely needs to be somehow managed by the caller. It's nasty and anti-LabVIEW like but sometimes almost unavoidable. The alternative would be to rewrite the DLL... [snip]
I'm pretty sure the clcamiface3.DLL came from Critical Link. Apparently it's used with multiple cameras, though you're right - it is not LabVIEW friendly. I had to develop a (.NET) wrapper in order to exploit the callback functionality, but that's another story...
02-01-2018 10:13 AM
@550nm wrote:
Hi clmartinez,
It seems "CL Read CCD Area" fills-in an array - is that right? If so, the "Data" array must be preallocated. If the calling VI isn't allocating the Data array, then I'm guessing this could be a problem. It would be easy to modify "CL Read CCD Area" to preallocate the array-space, but I don't know how big the array should be. Do you know how many pixels are expected and how many bytes-per-pixel? By the way, I didn't see an example program in the.zip file - can you attach it?
Hi There.
Yes! this was the fix. We added "initialize array" before reading any data, then replaced the initialized array with that data gathered by the CCD camera. We have tested it several different ways, and happily it solved the problem. Thank You!
02-01-2018 10:15 AM
@550nm wrote:
Hi clmartinez,
It seems "CL Read CCD Area" fills-in an array - is that right? If so, the "Data" array must be preallocated. If the calling VI isn't allocating the Data array, then I'm guessing this could be a problem. It would be easy to modify "CL Read CCD Area" to preallocate the array-space, but I don't know how big the array should be. Do you know how many pixels are expected and how many bytes-per-pixel? By the way, I didn't see an example program in the.zip file - can you attach it?
Yes! This was the solution. We added "initialize array" with the proper dimensions to the code, then replaced the array with the data gathered from the CCD camera. Works like a charm. THANK YOU!
02-01-2018 06:51 PM
You're welcome!
Happy to hear the good news!