LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

help with Keyence LS-9 CLF

Solved!
Go to solution

I have searched the forums extensively and tried over a dozen different configurations, but I cannot get the LS9IF_GetSettings function to operate properly for the Keyence LS-9501P Controller.  I am specifically trying to get the number of data points from the controller.  Through the various forum searches I have been able to successfully create function calls to the controller using the LS9_IF.dll for LS9IF_USBOpen, LS9IF_CommClose, LS9IF_GetVersion, LS9IF_ClearMemory, LS9IF_CheckMemoryAccess, LS9IF_StartStorage, LS9IF_StopStorage, LS9IF_GetStorageStatus, and LS9IF_GetStorageData.  All of these function calls work without issue.

 

While creating this post, I found the following post for the LS-9501, but doesn't end up with a resolution.  I didn't use the wizard to create any of the function calls above.

https://forums.ni.com/t5/LabVIEW/Data-loss-in-LV-when-reading-Keyence-LS-9501-measurements-via/m-p/3....

 

The attached vi (in LabVIEW15) shows all of the various configurations that I have tried in order to get this to work, none of them being successful.  I have copied the related sections of the LS9_IF.h header file, the sample code that calls the GetSetting function for the exact purpose of getting the number of data points, and the LS9 specific error code header file all onto the block diagram of the vi.  The executable built from the sample code works well.  I will put the sample code and the structure definition below for convenience.  If there is any other documentation that would be needed, please let me know and I will add it to the thread.

 

LS9_IF.h:

/// Structure of setting item specification
typedef struct {
BYTE byType; // Type
BYTE byCategory; // Category
BYTE byItem; // Item
BYTE reserve; // Reserve
BYTE byTarget; // Target
} LS9IF_TARGET_SETTING;

 

...

 

/**
Get setting
@param byDepth Setting value storage hierarchy
@param stTargetSetting Item setting
@param pbyDatas Buffer receiving the acquired setting data
@param plDataSize Data size
@return Return code
*/
LS9_IF_API LONG WINAPI LS9IF_GetSetting(BYTE byDepth,
LS9IF_TARGET_SETTING stTargetSetting,
BYTE* pbyDatas,
LONG* plDataSize);

 

SampleDlg.cpp:

/**
"Get(Settings(Program No.0))" button clicked
*/
void CSampleDlg::OnBnClickedButtonGetstoragepoints()
{
UpdateData(TRUE);

// Send the command
BYTE byDepth = 0x02;
LS9IF_TARGET_SETTING stTargetSetting;
stTargetSetting.byType = 0x10;
stTargetSetting.byCategory = 0x05;
stTargetSetting.byItem = 0x02;
stTargetSetting.byTarget = 0x00;
BYTE abyDatas[128] = {}; // We can read any setting if we secure 128byte
LONG lDataSize = 0;
if (!CheckReturnCode(LS9IF_GetSetting(byDepth, stTargetSetting, abyDatas, &lDataSize))) return;

// Output the result
DWORD nStoragePoint = *(DWORD*)abyDatas;
CString strStoragePoint;
strStoragePoint.Format(_T("%d"), nStoragePoint);
OutPutResult(IDC_STATIC_GRP_SETTINGS, IDC_BUTTON_GETSTORAGEPOINTS, strStoragePoint);
}

 

Thanks in advance for any help,

 

Scott

0 Kudos
Message 1 of 6
(3,354 Views)

As you can see in this declaration:

LS9_IF_API LONG WINAPI LS9IF_GetSetting(BYTE byDepth, 
                 LS9IF_TARGET_SETTING stTargetSetting, 
                 BYTE* pbyDatas, 
                 LONG* plDataSize);

the struct is passed by value and not by reference (missing asterix in front of the variable). This means that the contents in the structure is passed on the stack directly. The rather interesting part that I can not answer here now is, if the individual BYTEs in the cluster are all passed individually or if the cluster is packed so that you end up having an UINT32 passed as value with the first 4 BYTEs as one value and then another UINT8 value for the remaining byTarget variable.

 

I would tend to believe it is the second but that would also mean that this falls apart when you ever move to a 64-bit DLL.

 

The crooky part here is the DLL developer for sure. While you can pass structs by value in C it is very uncommon and in fact not standardized between compilers how this needs to be done (which is one of the reason LabVIEW doesn't even attempt to support this). But you can always configure the Call Library Node accordingly by splitting out the cluster into properly packed individual parameters. You just need to be aware that it will break when changing between 32-bit and 64-bit and can break when the DLL is created with a different compiler even if the bitness remains the same. 

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 2 of 6
(3,324 Views)

Some further information regarding bitness:

  • I am using 32bit LabVIEW on a 64 bit Windows 10 machine.
  • The executable that I am able to properly use is for the 32 bit driver and I was only able to make that work properly by emulating a Windows 8 machine.
  • I am using the 32 bit version of the LS9IF.dll because that is what is working with the executable.

Now that you mention the struct is being passed by reference, I realized that all of the other struct's are being passed by reference in the function calls that I was able to get working.

 

I tried to break out the structure with a reserve U8 byte between byItem and byTarget (for 4 byte alignment), as well as with an additional two reserve U8 bytes after byTarget (for 8 byte alignment).  I also tried the broken down structure without any reserve u* bytes (for sh*** and giggles).  They all resulted in the 1097 error (but not one of the standard Keyence return error codes) and no data.  Those were my best guesses at byte alignment.  I previously tried the same thing within a cluster as well, with the same result.

 

I'm not sure what else to try based on your response Rolf.  Does the information provided above help?

 

Could you explain the proper configuration regarding the dwDataSize(out) pointer (DWORD => U32)?  Because the array size is an output, does that indicate the function will modify the size of the array?  Would I then need to allocate the memory for a 128byte array but then only get the values from the size of the memory locations indicated by the output pointer?

0 Kudos
Message 3 of 6
(3,309 Views)
Solution
Accepted by topic author doyles

@doyles wrote:

Some further information regarding bitness:

  • I am using 32bit LabVIEW on a 64 bit Windows 10 machine.
  • The executable that I am able to properly use is for the 32 bit driver and I was only able to make that work properly by emulating a Windows 8 machine.
  • I am using the 32 bit version of the LS9IF.dll because that is what is working with the executable.

Now that you mention the struct is being passed by reference, I realized that all of the other struct's are being passed by reference in the function calls that I was able to get working.

 

I tried to break out the structure with a reserve U8 byte between byItem and byTarget (for 4 byte alignment), as well as with an additional two reserve U8 bytes after byTarget (for 8 byte alignment).  I also tried the broken down structure without any reserve u* bytes (for sh*** and giggles).  They all resulted in the 1097 error (but not one of the standard Keyence return error codes) and no data.  Those were my best guesses at byte alignment.  I previously tried the same thing within a cluster as well, with the same result.

 

I'm not sure what else to try based on your response Rolf.  Does the information provided above help?

I would personally expect that the 4 first values are simply packed in an UINT32 and then passed by value and the last value should be possible to be passed as an additional UINT8.

 

Could you explain the proper configuration regarding the dwDataSize(out) pointer (DWORD => U32)?  Because the array size is an output, does that indicate the function will modify the size of the array?  Would I then need to allocate the memory for a 128byte array but then only get the values from the size of the memory locations indicated by the output pointer?


Noooooo! A C function can not just go and resize an array pointer like that. Unless there is some specific non-standard memory management happening (which better should be documented in detail in the API documentation), the way this works is that the caller MUST allocate a buffer that is big enough for the function to use. Sometimes this is a specific size required by the function and hopefully properly documented, or you sometimes have to pass this size to the function, either by a separate by value parameter or as initial value to the by reference size value. Before returning he function writes into the buffer and then normally will write the number of bytes/elements/beers or whatever it put in the buffer into the by reference size parameter.

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 4 of 6
(3,299 Views)

@rolfk wrote:

I would personally expect that the 4 first values are simply packed in an UINT32 and then passed by value and the last value should be possible to be passed as an additional UINT8.

I packed the byType, byCategory, byItem and the reserve U8 into a U32 from lo to hi and it works!!!  Thank you very much Rolf!!  I would never have thought to join the first 4 bytes of that Struct.

 

Could you explain in further detail about how it will break between 32 bit and 64 bit?

 

Thanks again,

 

Scott

0 Kudos
Message 5 of 6
(3,283 Views)

@doyles wrote:

 

Could you explain in further detail about how it will break between 32 bit and 64 bit?

In 64-bit the register size is 64-bit long, and the first 5 parameters are passed through registers. So all 5 bytes will fit in a single register and therefore will need to be passed as one parameter by value.

That means even if the DLL is simply recompiled it will not be possible to use the same Call Library Node configuration and you will have to create two separate CLNs with different cofiguration and place them in a Conditional Compile Structure.

 

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 6 of 6
(3,279 Views)