# Process Data Access The process data that is exchanged between an EtherCAT® MainDevice and the SubDevices in each cycle is stored in the process data image. There are two separate memory areas, one for the input data and one for the output data. The base addresses of these areas are provided by calling the functions [`emonGetProcessImageInputPtr()`](api_processdata.html.md#_CPPv427emonGetProcessImageInputPtr10EC_T_DWORD) and [`emonGetProcessImageOutputPtr()`](api_processdata.html.md#_CPPv428emonGetProcessImageOutputPtr10EC_T_DWORD). The size of the process data input image is defined in the ENI file under `EtherCATConfig/Config/ProcessImage/Inputs/ByteSize` and `EtherCATConfig/Config/ProcessImage/Outputs/ByteSize` and is returned by [`emonRegisterClient()`](api_general.html.md#_CPPv418emonRegisterClient10EC_T_DWORD12EC_PF_NOTIFYP9EC_T_VOIDP20EC_T_REGISTERRESULTS) at [`EC_T_REGISTERRESULTS::dwPDOutSize`](api_general.html.md#_CPPv4N20EC_T_REGISTERRESULTS11dwPDOutSizeE) and [`EC_T_REGISTERRESULTS::dwPDInSize`](api_general.html.md#_CPPv4N20EC_T_REGISTERRESULTS10dwPDInSizeE). ## Process Data Access Functions Process data variables that are packed as array of bits are bit aligned and not byte aligned in process data. Accessing bits that are bit aligned and not byte aligned should be done using [`EC_GETBITS`](api_processdata.html.md#c.EC_GETBITS). See [`EC_COPYBITS`](api_processdata.html.md#c.EC_COPYBITS) for how to copy data areas with bit offsets that are not byte aligned. Corresponding aligned variables, e.g. of the types [`EC_T_BYTE`](api.html.md#_CPPv49EC_T_BYTE), [`EC_T_WORD`](api.html.md#_CPPv49EC_T_WORD), [`EC_T_DWORD`](api.html.md#_CPPv410EC_T_DWORD), [`EC_T_UINT64`](api.html.md#_CPPv411EC_T_UINT64), can be accessed more efficiently using the appropriate macros according to the following table. #### NOTE Process data is typically transmitted as little endian and must therefore be swapped on big endian systems in order to be correctly interpreted. | Variable type | Bit size | Macro | Hint | |----------------------------------------------------------------------------------|------------|--------------------------------------------------------------------------------------|-----------------------------------------------------------| | Bit | 1 | [`EC_GETBITS`](api_processdata.html.md#c.EC_GETBITS) | Contains swap for big endian systems | | [`EC_T_BYTE`](api.html.md#_CPPv49EC_T_BYTE) | 8 | N/A | Bytes can be directly addressed at pbyBuffer[BitOffset/8] | | [`EC_T_WORD`](api.html.md#_CPPv49EC_T_WORD) | 16 | [`EC_GET_FRM_WORD`](api_processdata.html.md#c.EC_GET_FRM_WORD) | Contains swap for big endian systems | | [`EC_T_DWORD`](api.html.md#_CPPv410EC_T_DWORD) | 32 | [`EC_GET_FRM_DWORD`](api_processdata.html.md#c.EC_GET_FRM_DWORD) | Contains swap for big endian systems | | [`EC_T_UINT64`](api.html.md#_CPPv411EC_T_UINT64) | 64 | [`EC_GET_FRM_QWORD`](api_processdata.html.md#c.EC_GET_FRM_QWORD) | Contains swap for big endian systems | ## Process variables’ offset and size The following screenshot shows variables’ offset and size within the Process Data Image: ![EC-Engineer screenshot showing SubDevice process variables with their bit offset and size values in the Process Data Image](../Media/PdAccess_PdVariablesList.png) Accessing the process data of a specific SubDevice always works by adding an offset to the base address. All offsets are given as bit offsets! There are different ways possible to get this offset. The offset values will not change until a new configuration is provided, therefore it is sufficient to load them once right after [`emonConfigureNetwork()`](api_general.html.md#_CPPv420emonConfigureNetwork10EC_T_DWORD13EC_T_CNF_TYPE10EC_T_PBYTE10EC_T_DWORD), it is not needed every cycle. ## Process variable access via hard coded offsets The offset value can be determined from an EtherCAT® configuration tool. It is not recommended to use fixed values as the offsets will change as SubDevices are added/removed from the configuration. As shown in the screenshot above, SubDevice_1004 [EL2004].Channel 3.Output is at offset 1.2 with size 0.1 in the example. The numbering is Byte.Bit so the offset in the example is Byte 1, Bit 2, Bit offset: : $8*1+2 = 10$ Bit size : $0*8+1 = 1$ ```cpp EC_T_BYTE* pbyPdOut = emonGetProcessImageInputPtr(dwInstanceId); EC_T_BYTE byValue = 0x00; EC_T_DWORD dwBitOffset = 10; EC_T_DWORD dwBitSize = 1; /* get variable in process data */ EC_GETBITS(pbyPdOut, &byValue, dwBitOffset, dwBitSize); ``` ## Process variable access via generated PD Layout The EC-Engineer / EC-Inspector can export the process variables to a PD-Layout C-Header via the menu item Network ‣ Export Process Variables as shown in the following screenshots: ![EC-Engineer Network menu showing the Export Process Variables option for generating a PD Layout C header file](../Media/PdAccess_ExportProcVar1.png)![EC-Engineer showing the Export Process Data Image dialog](../Media/PdAccess_ExportProcVar2.png) This will generate a header file containing the SubDevices’ variables as follows: ```cpp #include EC_PACKED_INCLUDESTART(1) #define PDLAYOUT_OUT_OFFSET_SLAVE_2002 22 typedef struct _T_PDLAYOUT_OUT_SLAVE_2002 { EC_T_SWORD swChannel_1_Output; // Slave_2002 [EL4132].Channel 1.Output ... EC_T_SWORD swChannel_2_Output; // Slave_2002 [EL4132].Channel 2.Output ... } EC_PACKED(1) T_PDLAYOUT_OUT_SLAVE_2002; #include EC_PACKED_INCLUDESTOP ``` Example how a value can be accessed: ```cpp EC_T_BYTE* pbyPdOut = emonGetProcessImageOutputPtr(dwInstanceId); T_PDLAYOUT_OUT_SLAVE_2002* pPdOutSlave2002 = (T_PDLAYOUT_OUT_SLAVE_2002*)(pbyPdOut + PDLAYOUT_OUT_OFFSET_SLAVE_2002); EC_T_WORD wChannel1Out = EC_GET_FRM_WORD(&pPdOutSlave2002->swChannel_1_Output); ``` ## Process variable access dynamically from ENI ### emonGetCfgSlaveInfo The SubDevice offsets can be determined dynamically with the function [`emonGetCfgSlaveInfo()`](api_slave.html.md#_CPPv419emonGetCfgSlaveInfo10EC_T_DWORD9EC_T_BOOL9EC_T_WORDP19EC_T_CFG_SLAVE_INFO). The offsets are stored in [`EC_T_CFG_SLAVE_INFO::dwPdOffsIn`](api_slave.html.md#_CPPv4N19EC_T_CFG_SLAVE_INFO10dwPdOffsInE) and [`EC_T_CFG_SLAVE_INFO::dwPdOffsOut`](api_slave.html.md#_CPPv4N19EC_T_CFG_SLAVE_INFO11dwPdOffsOutE). Example of how SubDevice_1004 [EL2004].Channel 3.Output can be accessed: ```cpp EC_T_CFG_SLAVE_INFO SlaveInfo; dwRes = emonGetCfgSlaveInfo(dwInstanceId, EC_TRUE, 1004, &SlaveInfo); EC_T_BYTE* pbyPdOut = emonGetProcessImageOutputPtr(dwInstanceId); EC_T_BYTE byValue = 0x00; EC_T_DWORD dwBitOffset = SlaveInfo.dwPdOffsOut + 2; EC_T_DWORD dwBitSize = 1; /* get variable in process data */ EC_GETBITS(pbyPdOut, &byValue, dwBitOffset, dwBitSize); ``` ### emonGetSlaveOutpVarInfo All variables of a specific SubDevice can be determined dynamically with the functions [`emonGetSlaveInpVarInfoEx()`](api_slave.html.md#_CPPv424emonGetSlaveInpVarInfoEx10EC_T_DWORD9EC_T_BOOL9EC_T_WORD9EC_T_WORDP24EC_T_PROCESS_VAR_INFO_EXP9EC_T_WORD) or [`emonGetSlaveOutpVarInfoEx()`](api_slave.html.md#_CPPv425emonGetSlaveOutpVarInfoEx10EC_T_DWORD9EC_T_BOOL9EC_T_WORD9EC_T_WORDP24EC_T_PROCESS_VAR_INFO_EXP9EC_T_WORD). The offset is stored in [`EC_T_PROCESS_VAR_INFO_EX::nBitOffs`](api_slave.html.md#_CPPv4N24EC_T_PROCESS_VAR_INFO_EX8nBitOffsE). Example of how SubDevice_1004 [EL2004].Channel 3.Output can be accessed: ```cpp EC_T_WORD wNumSlaveVars = 0; EC_T_WORD wNumVarsRead = 0; EC_T_PROCESS_VAR_INFO_EX* aProcVarInfo = EC_NULL; /* get number of output variables */ dwRes = emonGetSlaveOutpVarInfoNumOf(dwInstanceId, EC_TRUE, 1004, &wNumSlaveVars); /* allocate buffer for the variable info structs */ aProcVarInfo = (EC_T_PROCESS_VAR_INFO_EX*)OsMalloc(sizeof(EC_T_PROCESS_VAR_INFO_EX) * wNumSlaveVars); OsMemset(aProcVarInfo, 0, sizeof(EC_T_PROCESS_VAR_INFO_EX) * wNumSlaveVars); /* read all variables of the slave at once */ dwRes = emonGetSlaveOutpVarInfoEx(dwInstanceId, EC_TRUE, 1004, wNumSlaveVars, aProcVarInfo, &wNumVarsRead); EC_T_BYTE* pbyPdOut = emonGetProcessImageOutputPtr(dwInstanceId); EC_T_BYTE byValue = 0x00; /* get variable in process data */ EC_GETBITS(pbyPdOut, &byValue, aProcVarInfo[0].nBitOffs, aProcVarInfo[0].nBitSize); ``` ### emonFindOutpVarByName The variable offsets can be determined dynamically using the names with the functions [`emonFindInpVarByNameEx()`](api_processdata.html.md#_CPPv422emonFindInpVarByNameEx10EC_T_DWORDPK9EC_T_CHARP24EC_T_PROCESS_VAR_INFO_EX) or [`emonFindOutpVarByNameEx()`](api_processdata.html.md#_CPPv423emonFindOutpVarByNameEx10EC_T_DWORDPK9EC_T_CHARP24EC_T_PROCESS_VAR_INFO_EX). Each input or output has a unique variable name, all variable names are stored in the ENI file under `EtherCATConfig/Config/ProcessImage/[Inputs|Outputs]/Variable`. The offset is stored in [`EC_T_PROCESS_VAR_INFO_EX::nBitOffs`](api_slave.html.md#_CPPv4N24EC_T_PROCESS_VAR_INFO_EX8nBitOffsE). Example of how SubDevice_1004 [EL2004].Channel 3.Output can be accessed: ```cpp EC_T_PROCESS_VAR_INFO_EX ProcVarInfo; dwRes = emonFindOutpVarByNameEx(dwInstanceId, "Slave_1004 [EL2004].Channel 3.Output", &ProcVarInfo); EC_T_BYTE* pbyPdOut = emonGetProcessImageOutputPtr(dwInstanceId); EC_T_BYTE byValue = 0x00; /* get variable in process data */ EC_GETBITS(pbyPdOut, &byValue, ProcVarInfo.nBitOffs, ProcVarInfo.nBitSize); ``` ### emonGetSlaveOutpVarByObjectEx The variable offsets can be determined dynamically using the object index and subindex with the functions [`emonGetSlaveInpVarByObjectEx()`](api_slave.html.md#_CPPv428emonGetSlaveInpVarByObjectEx10EC_T_DWORD9EC_T_BOOL9EC_T_WORD9EC_T_WORD9EC_T_WORDP24EC_T_PROCESS_VAR_INFO_EX) or [`emonGetSlaveOutpVarByObjectEx()`](api_slave.html.md#_CPPv429emonGetSlaveOutpVarByObjectEx10EC_T_DWORD9EC_T_BOOL9EC_T_WORD9EC_T_WORD9EC_T_WORDP24EC_T_PROCESS_VAR_INFO_EX). The object index and subindex can be got with EC-Engineer: ![EC-Engineer showing SubDevice object index and subindex values for dynamic process variable access via emonGetSlaveOutpVarByObjectEx](../Media/PdAccess_ObjIdx.png) Example of how SubDevice_1004 [EL2004].Channel 3.Output can be accessed: ```cpp EC_T_PROCESS_VAR_INFO_EX ProcVarInfo; dwRes = emonGetSlaveOutpVarByObjectEx(dwInstanceId, EC_TRUE, 1004, 0x7010, 0x01, &ProcVarInfo); EC_T_BYTE* pbyPdOut = emonGetProcessImageOutputPtr(dwInstanceId); EC_T_BYTE byValue = 0x00; /* get variable in process data */ EC_GETBITS(pbyPdOut, &byValue, ProcVarInfo.nBitOffs, ProcVarInfo.nBitSize); ```