【EtherCATBasics】- KRTS C++示例精讲(2)
EtherCATBasics示例讲解
目录
- EtherCATBasics示例讲解
- 结构说明
- 代码讲解
项目打开请查看【BaseFunction精讲】。
结构说明
-
EtherCATBasics:应用层程序,主要用于人机交互、数据显示、内核层数据交互等;
- EtherCATBasics.h : 数据定义
- EtherCATBasics.cpp:用户应用层源码
-
EtherCATBasics_64: 内核层程序(实时层程序),主要用于实时数据处理;
- EtherCATBasics.h : 数据定义
- EtherCATBasics_dll.cpp : 内核层源码
-
其余文件说明请查看【BaseFunction精讲】中的结构说明。
ps : 内核层中的数据、结构体需要一字节对齐,需要以MT方式构建
代码讲解
EtherCATBasics.h :应用层与内核层共用一个头文件
/* Copyright (c) 2011-2024 by Kithara Software GmbH. All rights reserved. *///##############################################################################################################
//
// 文件: EtherCATBasics.h
//
// 使用模块: EtherCAT模块, 时钟模块, 网络模块, 实时模块
//
// 用户应用程序和内核DLL之间的共享定义,例如:如何通过EtherCAT实现基本进程数据交换的示例
//
// 开发者: m.gru 2011-05-11
//
//##############################################################################################################/*=====================================================================*\| *** 免责声明 *** || || 本代码仅是示例程序,您可以随意使用,我们不承担任何法律责任! || |\*=====================================================================*///##############################################################################################################
//
// 目的:
//
// 该示例说明了如何在不考虑同步读取进程数据的情况下实现基本的进程数据交换。
// 此外,该示例还展示了如何检测从站可用的分布式时钟功能以及如何利用这些功能。
// 首先,我们打开网络作为 EtherCAT 主站,并创建一个 EtherCAT 主站。
// 然后,我们让用户从连接的 EtherCAT 从站中选择一个,并将其映射到创建的数据集。
// 如果所选的 EtherCAT 从站支持分布式时钟 (DC) 功能,用户可以选择其中一种支持的模式。
// 然后,用户必须从从站的一个 PDO 中选择一个变量。
// 将创建一个定时器,触发拓扑(已连接的 EtherCAT从站)与本示例实例化的 EtherCAT 主站之间的数据交换。
// 所选变量每 100ms 显示一次(定时器每 1ms 接收一次)。
//
//###############################################################################################################ifndef __SMP_ETHERCATBASICS_H
#define __SMP_ETHERCATBASICS_H#include "../_KitharaSmp/_KitharaSmp.h"//--------------------------------------------------------------------------------------------------------------
// SharedData 是用户定义的参数结构,用于在内核 DLL 和用户应用程序之间使用共享内存交换信息。
//--------------------------------------------------------------------------------------------------------------struct SharedData {KSHandle hKernel; // 内核句柄KSHandle hAdapter; // 网络适配器句柄KSHandle hMaster; // EtherCAT主站句柄KSHandle hSlave; // EtherCAT从站句柄KSEcatMasterState masterState; // 获取主站状态的结构体KSHandle hDataSet; // 用于主站和拓扑结构之间交换的数据集的句柄KSHandle hDataSetCallBack; // 回调句柄,当数据集从拓扑返回时将被调用KSHandle hTimerCallBack; // 由定时器调用的回调的句柄KSHandle hTimer; // 将调用 DataSet 的计时器的句柄int varIndex; // 需要查看的变量的索引int varSubIndex; // 需要查看的变量的子索引uint data; // 从 DataSet 复制的数据,供应用程序访问KSError error; // 用于从内核空间 dll 向用户空间应用程序传递错误信息
};#endif // __SMP_ETHERCATBASICS_H
EtherCATBasics.cpp
/* Copyright (c) 2009-2024 by Kithara Software GmbH. All rights reserved. *///##############################################################################################################
//
// 文件: EtherCATBasics.cpp
//
// 使用模块: EtherCAT模块, 时钟模块, 网络模块, 实时模块
//
// 用户应用程序和内核DLL之间的共享定义,例如:如何通过EtherCAT实现基本进程数据交换的示例
//
// 开发者: t.pet 2009-08-26
//
//##############################################################################################################/*=====================================================================*\| *** 免责声明 *** || || 本代码仅是示例程序,您可以随意使用,我们不承担任何法律责任! || |\*=====================================================================*///##############################################################################################################
//
// 目的:
//
// 该示例说明了如何在不考虑同步读取进程数据的情况下实现基本的进程数据交换。
// 此外,该示例还展示了如何检测从站可用的分布式时钟功能以及如何利用这些功能。
// 首先,我们打开网络作为 EtherCAT 主站,并创建一个 EtherCAT 主站。
// 然后,我们让用户从连接的 EtherCAT 从站中选择一个,并将其映射到创建的数据集。
// 如果所选的 EtherCAT 从站支持分布式时钟 (DC) 功能,用户可以选择其中一种支持的模式。
// 然后,用户必须从从站的一个 PDO 中选择一个变量。
// 将创建一个定时器,触发拓扑(已连接的 EtherCAT从站)与本示例实例化的 EtherCAT 主站之间的数据交换。
// 所选变量每 100ms 显示一次(定时器每 1ms 接收一次)。
//
//##############################################################################################################//--------------------------------------------------------------------------------------------------------------
// 为了在主程序(用户层)和内核 DLL 之间共享数据结构的定义,我们使用了一个通用的头文件。
//--------------------------------------------------------------------------------------------------------------#include "EtherCATBasics.h"//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// 别忘了输入你的序列号(6位客户编号),这是打开驱动程序所必需的。
//
// 如果你使用Demo版本,也可以使用“DEMO”代替。
// 如果你使用Beta版本,也可以使用“BETA”代替。
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!// 如上说所,定义的客户号
const char _pCustomerNumber[] = "DEMO";// 主程序入口
void runSample() {// 调用KitharaSmp.h 中的函数,输出文本outputTxt("***** Kithara example program 'EtherCATBasics' *****");// 错误码定义,KSError 是 Kithara API 所有函数的返回类型,通过 【KSError】 可以查询接口的返回错误信息。KSError ksError;//------------------------------------------------------------------------------------------------------------// 打开驱动程序的第一步,所有KRTS程序必须进行的操作。// 只要该函数调用成功后,我们可以使用其他函数。如果打开失败,则无法调用其他函数。// 此函数接受您的客户编号作为参数,其中包含 Kithara(如果适用可以为“DEMO”或“BETA”)。//------------------------------------------------------------------------------------------------------------ksError = KS_openDriver(_pCustomerNumber); // 客户编号if (ksError != KS_OK) {outputErr(ksError, "KS_openDriver", "Unable to open the driver!");return;}//------------------------------------------------------------------------------------------------------------// 创建共享内存// 为实时层中的DLL和此用户层应用程序之间的通信。//------------------------------------------------------------------------------------------------------------KSHandle hSharedMemory;ksError = KS_createSharedMemEx(&hSharedMemory, // 返回创建的共享内存句柄"", // 共享内存的名称sizeof(SharedData), // 共享内存的大小KSF_NO_FLAGS); // 无标记,此选项可以进行一些特殊设定if (ksError != KS_OK) {outputErr(ksError, "KS_createSharedMemEx", "Unable to create shared memory!");KS_closeDriver();return;}// 要访问共享内存,应用程序需要使用刚创建的共享内存的句柄来获取指向分配的共享内存的指针。SharedData* pApp = NULL; // 自定义的共享内存结构体ksError = KS_getSharedMemEx(hSharedMemory, // 共享内存的句柄(void**)&pApp, // 指向共享内存的结构的指针KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_getSharedMemEx", "Unable to map shared memory!");KS_closeDriver();return;}// 确定操作系统的位数大小以决定是加载32位还是64位内核DLL。KSSystemInformation systemInfo; // 获取系统信息的结构体systemInfo.structSize = sizeof(KSSystemInformation); // 不要忘记设备结构体大小ksError = KS_getSystemInformation(&systemInfo, // 结构体指针用于获取结构体数据KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_getSystemInformation", "Unable to get system information to distinguish bitsize!");KS_closeDriver();return;}//------------------------------------------------------------------------------------------------------------// 想要在内核级别上使用DLL中的函数,必须加载dll,在调用里面的函数!// 注意!加载程序必须找到DLL,因此它应该放在搜索路径的目录中!// 因为我们想要在共享内存中传递加载的内核的句柄,所以我们不使用加载内核时的初始化函数,// 而是在填充内核句柄和初始化内核所需的所有信息之后,显式调用初始化函数。//------------------------------------------------------------------------------------------------------------ksError = KS_loadKernel(&pApp->hKernel, // 返回内核操作句柄systemInfo.isSys64Bit ? // 根据系统位数加载内核Dll"EtherCATBasics_64.dll" : "EtherCATBasics_32.dll", NULL, // 需要支持的函数名称(未使用)NULL, // 函数参数 (未使用)KSF_KERNEL_EXEC); // 内核空间中加载(实时层运行)if (ksError != KS_OK) {outputErr(ksError, "KS_loadKernel", "Unable to load DLL! Is the DLL in the search path?");KS_closeDriver();return;}// 查询并展示所有受支持的网络适配器char pDeviceName[256]; // 用于保存设备名称outputTxt(" ");outputTxt("Following network adapters found:");for (int i = 0;; ++i) {// KS_enumDevices()可以用于查询分配给Kithara驱动程序的所有网络适配器的名称。ksError = KS_enumDevices("NET", // 'NET' 代表搜索网络设备i, // 从0开始的枚举索引号pDeviceName, // 返回设备名称KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {if (KSERROR_CODE(ksError) != KSERROR_DEVICE_NOT_FOUND)outputErr(ksError, "KS_enumDevices", "Unable to query network device name!");if (KSERROR_CODE(ksError) == KSERROR_DEVICE_NOT_FOUND && !i) {outputTxt("No network adapters found!");outputTxt(" ");KS_closeDriver();return;}break;}// 输出索引号对应的设备名称outputDec(i, "", ": ", false);outputTxt(pDeviceName);}outputTxt(" ");// 输入想要打开的适配器索引号outputTxt("Attention!");outputTxt("By selecting a device its Windows driver gets removed and replaced by the");outputTxt("appropriate Kithara driver. This will render the network device for the duration");outputTxt("of this sample invisible to Windows.");outputTxt("Be sure that all other Applications using that device are closed right now!");// 输入并保存索引号并根据索引和再次获取设备名称int deviceIndex = inputDec("Device number: ", 0);ksError = KS_enumDevices("NET", // 'NET' 代表搜索网络设备deviceIndex, // 选择的设备索引号pDeviceName, // 返回设备名称KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_enumDevices", "Unable to query selected network device name!");KS_closeDriver();return;}outputTxt("Selected device: ", false);outputTxt(pDeviceName);outputTxt(" ");// 根据设备名称,打开以太网适配器。ksError = KS_openNetworkAdapter(&pApp->hAdapter, // 获取适配器句柄pDeviceName, // 输入适配器的硬件IDNULL, // 设配器配置选项KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_openNetworkAdapter", "Failed to open network adapter!");KS_closeDriver();return;}// 创建主站// 输入ESI文件,文件夹路径,ESI文件可以在大多数设备官网中获取。 char* pXmlPath = inputTxt("Please enter config path to XML files: ", "C:\\Program Files\\Kithara\\RealTime Suite Demo\\xml");ksError = KS_createEcatMaster(&pApp->hMaster, // 返回主站句柄pApp->hAdapter, // 适配器句柄pXmlPath, // ESI文件夹路径"", // 拓扑文件KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_createEcatMaster", "Failed to create EtherCAT master!");KS_closeDriver();return;}//------------------------------------------------------------------------------------------------------------// 显式调用初始化函数。// 调用内核的【_initFunction】函数,并传递共享内存的句柄,这样内核就可以从句柄中检索到共享内存的指针,// 并根据共享内存中存储的信息进行所有必要的资源分配。// 更为详细的内核初始化操作可以查看内核层【_initFunction】函数//------------------------------------------------------------------------------------------------------------ksError = KS_execKernelFunctionEx(pApp->hKernel, // 内核句柄"_initFunction", // 函数名称hSharedMemory, // 共享内存的句柄KS_INVALID_HANDLE, // 上下文KSF_NO_FLAGS); // 未使用if (ksError != KS_OK) {outputErr(ksError, "KS_execKernelFunctionEx", "Unable to initialize the kernel DLL!");KS_closeDriver();return;}bool dcLicensed = true; // 是否支持DC模式// 输出从站在线个数outputDec(pApp->masterState.slavesOnline, "slavesOnline = "); outputTxt("Choose a slave: ");KSEcatSlaveState slaveState; // 从站状态的结构体slaveState.structSize = sizeof(KSEcatSlaveState); // 初始化结构体大小// 遍历所有已连接的从设备,显示它们的基本信息,让用户选择其中一个。for (int i = 0; i < pApp->masterState.slavesOnline; ++i) {//----------------------------------------------------------------------------------------------------------// 使用KS_enumEcatSlaves()函数来迭代所有在线的从站设备。// 如果它的第二个参数大于在线从设备的数量,它将返回KSERROR_DEVICE_NOT_FOUND。//----------------------------------------------------------------------------------------------------------ksError = KS_enumEcatSlaves(pApp->hMaster, // EtherCAT 主站句柄i, // 从0开始的枚举索引号&slaveState, // 从站状态结构体KSF_NO_FLAGS); // 无标记if (KSERROR_CODE(ksError) == KSERROR_DEVICE_NOT_FOUND)break;if (ksError != KS_OK) {outputErr(ksError, "KS_enumEcatSlaves", "Failed to enumerate EtherCAT slaves!");KS_closeDriver();return;}//----------------------------------------------------------------------------------------------------------// 创建从站// KS_enumEcatSlaves()函数填充了KSEcatSlaveState结构体,其中包含了从设备的基本信息。// 为了获取更多的详细信息,需要为该从站设备创建一个句柄。// 可以直接将接收到的KSEcatSlaveState结构体传递给KS_createEcatSlaveIndirect()函数。//----------------------------------------------------------------------------------------------------------KSHandle hSlave;ksError = KS_createEcatSlaveIndirect(pApp->hMaster, // 主站句柄&hSlave, // 返回新从站句柄&slaveState, // 返回从站信息KSF_FORCE_OVERRIDE); // 忽略缺少的XML信息if (ksError != KS_OK) {outputErr(ksError, "KS_createEcatSlaveIndirect", "Failed to create EtherCAT slave!");KS_closeDriver();return;}// 修改从站状态,只有在等于或高于的KS_ECAT_STATE_PREOP状态下,才能查询在线从机信息。ksError = KS_changeEcatState(hSlave, // 从站句柄KS_ECAT_STATE_PREOP, // 需要切换的状态KSF_NO_FLAGS); // 无标志if (ksError != KS_OK) {outputErr(ksError, "KS_changeEcatState", "Failed to change EtherCAT slave state to PREOP!");KS_closeDriver();return;}//----------------------------------------------------------------------------------------------------------// 查询从站信息// KS_queryEcatSlaveInfo()函数将KSEcatSlaveInfo结构填充了所有可用的信息。// 通过使用KSF_PDO和KSF_SDO标志,您可以决定是否想要有关过程数据对象、服务数据对象或两者的信息。// 因为我们只对从设备的名称感兴趣,而不需要对象信息,所以我们使用flags == KSF_NO_FLAGS。//----------------------------------------------------------------------------------------------------------KSEcatSlaveInfo* pSlaveInfo;ksError = KS_queryEcatSlaveInfo(hSlave, // 从站句柄&pSlaveInfo, // 返回从站信息KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_queryEcatSlaveInfo", "Unable to read slave information!");KS_closeDriver();return;}//----------------------------------------------------------------------------------------------------------// 为了测试一个从节点是否支持分布式时钟,我们只需要枚举分布式时钟的操作模式。// 如果索引等于0,则该从节点支持分布式时钟。//----------------------------------------------------------------------------------------------------------char pDcOpMode[256];ksError = KS_enumEcatDcOpModes(hSlave, // 从站句柄0, // OP模式pDcOpMode, // 模式名称NULL, // 模式描述KSF_NO_FLAGS); // 无标记if (KSERROR_CODE(ksError) == KSERROR_FEATURE_NOT_LICENSED) {ksError = KS_OK;dcLicensed = false;}if (ksError != KS_OK && KSERROR_CODE(ksError) != KSERROR_DEVICE_NOT_FOUND) {outputErr(ksError, "KS_enumEcatDcOpModes", "Failed to query EtherCAT DC op mode");KS_closeDriver();return;}// 输出从站索引以及从站是否支持分布式时钟和从站名称。if (KSERROR_CODE(ksError) == KSERROR_DEVICE_NOT_FOUND || !dcLicensed)outputDec(i, "", ": [noDC] ", false);elseoutputDec(i, "", ": [DC] ", false);outputTxt(pSlaveInfo->name);// 删除用于查询从属信息以供显示从站对象。ksError = KS_deleteEcatSlave(hSlave); // 需要删除的从站句柄if (ksError != KS_OK) {outputErr(ksError, "KS_deleteEcatSlave", "Unable to delete slave!");KS_closeDriver();return;}}// 请输入选择的从站,已查询从站信息int slaveIndex = inputDec("Slave index: ", -1);if (slaveIndex < 0 || slaveIndex >= pApp->masterState.slavesOnline) {outputTxt("Invalid slave index!");KS_closeDriver();return;}//------------------------------------------------------------------------------------------------------------// 在指定的索引处创建一个 ECAT 从节点设备。// 只用索引,不需要 ID、厂商 ID、产品 ID 或修订号。//------------------------------------------------------------------------------------------------------------// 创建从站 ksError = KS_createEcatSlave(pApp->hMaster, // 主站句柄&pApp->hSlave, // 返回从站句柄0, // 该位置相对于的标识符,0表示绝对位置slaveIndex, // 从站位置0, // 供应商 ID(0 = any)0, // 产品ID (0 = any)0, // 版本(0 = any)KSF_FORCE_OVERRIDE); // 忽略缺少的XML信息if (ksError != KS_OK) {outputErr(ksError, "KS_createEcatSlave", "Failed to create EtherCAT slave!");KS_closeDriver();return;}// 只有在KS_ECAT_STATE_PREOP等于或高于的状态下,才能查询在线从机信息。ksError = KS_changeEcatState(pApp->hSlave, // 从站句柄KS_ECAT_STATE_PREOP, // 需要修改的从站状态KSF_NO_FLAGS); // 无状态if (ksError != KS_OK) {outputErr(ksError, "KS_changeEcatState", "Failed to change EtherCAT slave state to PREOP!");KS_closeDriver();return;}// 如果DC功能可用,则显示所有可用的分布式时钟操作模式。if (dcLicensed) {char pDcOpMode[256];char pDcOpModeDescription[256];outputTxt(" ");int dcModeCount = -1;for (int i = 0;; ++i) {//--------------------------------------------------------------------------------------------------------// 使用 KS_enumEcatDcOpModes() 函数,您可以枚举所有可用的分布式时钟操作模式。// 我们使用此函数来显示所有可用的分布式时钟操作模式,以便让用户选择其中一个。//--------------------------------------------------------------------------------------------------------ksError = KS_enumEcatDcOpModes(pApp->hSlave, // 从站句柄i, // 从零开始枚举索引pDcOpMode, // 获取DC模式名称pDcOpModeDescription, // 获取DC模式描述信息KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {if (KSERROR_CODE(ksError) != KSERROR_DEVICE_NOT_FOUND) {outputErr(ksError, "KS_enumEcatDcOpModes", "Unable to query DC op mode!");KS_closeDriver();return;}break;}dcModeCount = i;if (i == 0)outputTxt("Following DC op modes where found:");outputDec(i, "", ": ", false);outputTxt(pDcOpMode, false);outputTxt(" - ", false);outputTxt(pDcOpModeDescription);}//----------------------------------------------------------------------------------------------------------// 如果有 DC 操作可用,让用户通过索引选择一个并验证选择。//----------------------------------------------------------------------------------------------------------if (dcModeCount >= 0) {int dcOpModeIndex = inputDec("Op mode number: ", -1);outputTxt(" ");ksError = KS_enumEcatDcOpModes(pApp->hSlave, // 从站句柄dcOpModeIndex , // 索引pDcOpMode, // 获取DC模式名称pDcOpModeDescription, // 获取DC模式描述信息KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_enumEcatDcOpModes", "Unable to query DC op mode!");KS_closeDriver();return;}outputTxt("Selected op mode: ", false);outputTxt(pDcOpMode, false);outputTxt(" - ", false);outputTxt(pDcOpModeDescription, false);outputTxt(" ");// 获取DC操作模式的参数。KSEcatDcParams dcParams;ksError = KS_lookupEcatDcOpMode(pApp->hSlave, // 从站句柄pDcOpMode, // 操作模式&dcParams, // 返回DC模式参数KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_lookupEcatDcOpMode", "Unable to lookup DC op mode!");KS_closeDriver();return;}// 根据需要调整参数。// ...// 如果配置了从站节点以使用此DC操作模式(可能调整了dcParams)。ksError = KS_configEcatDcOpMode(pApp->hSlave, // 从站句柄pDcOpMode, // 模式名称&dcParams, // DC参数设定KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_configEcatDcOpMode", "Unable to lookup DC op mode!");KS_closeDriver();return;}}}//------------------------------------------------------------------------------------------------------------// 现在将为所选的从属节点分配DataSet。// 只要KS_assignEcatDataSet()没有返回错误,我们就可以分配对象。// 简化同步对象的选择,使用提供的特殊常量:KS_ECAT_SYNC_INPUT、KS_ECAT_SYNC_OUTPUT和KS_ECAT_SYNC_ALL。//------------------------------------------------------------------------------------------------------------ksError = KS_assignEcatDataSet(pApp->hDataSet, // DataSet 句柄pApp->hSlave, // 从站句柄KS_ECAT_SYNC_ALL, // 同步对象的类型0, // 在DataSet中的特殊位置KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_assignEcatDataSet", "Failed to assign slave!");KS_closeDriver();return;}//------------------------------------------------------------------------------------------------------------// KS_queryEcatSlaveInfo()用所有可用信息填充KSEcatSlaveInfo结构。// 使用Flags KSF_PDO和KSF_SDO可以决定是否需要有关PDO,SDO对象或两者。//------------------------------------------------------------------------------------------------------------KSEcatSlaveInfo* pSlaveInfo;ksError = KS_queryEcatSlaveInfo(pApp->hSlave, // 从站句柄&pSlaveInfo, // 从站信息的结构体KSF_PDO); // 查询PDO数据if (ksError != KS_OK) {outputErr(ksError, "KS_queryEcatSlaveInfo", "Unable to read slave information!");KS_closeDriver();return;}//------------------------------------------------------------------------------------------------------------// 让用户选择一个PDO对象。//------------------------------------------------------------------------------------------------------------// 遍历并输出PDO信息outputTxt(" ");outputTxt("Choose a PDO: (only active and readable PDOs are displayed)");for (int i = 0; i < pSlaveInfo->objCount; ++i) {KSEcatDataObjInfo* pObjInfo = pSlaveInfo->objs[i];if ((pObjInfo->objType & KS_DATAOBJ_PDO_TYPE) &&(pObjInfo->objType & KS_DATAOBJ_ACTIVE) &&(pObjInfo->objType & KS_DATAOBJ_READABLE)) {outputDec(i, "", ": ", false);outputTxt(pObjInfo->name);}}int pdoIndex = inputDec("PDO index: ", -1);if (pdoIndex < 0 || pdoIndex >= pSlaveInfo->objCount) {outputTxt("Invalid PDO index!");KS_closeDriver();return;}KSEcatDataObjInfo* pObjInfo = pSlaveInfo->objs[pdoIndex];if (!(pObjInfo->objType & KS_DATAOBJ_PDO_TYPE) ||!(pObjInfo->objType & KS_DATAOBJ_ACTIVE) ||!(pObjInfo->objType & KS_DATAOBJ_READABLE)) {outputTxt("Invalid PDO!");KS_closeDriver();return;}outputTxt(" ");outputTxt("Choose a variable: ");for (int i = 0; i < pObjInfo->varCount; ++i) {KSEcatDataVarInfo* pVarInfo = pObjInfo->vars[i];outputDec(i, "", ": ", false);outputTxt(pVarInfo->name);}int varIndex = inputDec("Variable index: ", -1);if (varIndex < 0 || varIndex >= pObjInfo->varCount) {outputTxt("Invalid variable index!");KS_closeDriver();return;}pApp->varIndex = pObjInfo->index;pApp->varSubIndex = pObjInfo->vars[varIndex]->subIndex;//------------------------------------------------------------------------------------------------------------// 开始进行数据交换//------------------------------------------------------------------------------------------------------------ksError = KS_execKernelFunctionEx(pApp->hKernel, // 内核句柄"_startDataExchange", // 调用内核层函数KS_INVALID_HANDLE, // 传参KS_INVALID_HANDLE, // 上下文KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_execKernelFunctionEx", "Error while executing kernel functions!");KS_closeDriver();return;}//------------------------------------------------------------------------------------------------------------// 这是主循环。它将每100 ms更新所选变量的显示,直到用户按下“q”键或数据交换错误将被检测到时退出。//------------------------------------------------------------------------------------------------------------outputTxt(" ");outputTxt("Press [Q] to finish the process data exchange...");outputTxt(" ");for (;;) {waitTime(100 * ms);outputHex08(pApp->data, "Value: ", "\r", false);if (pApp->error != KS_OK)break;if (myKbhit() != 0) {int key = myGetch();if (key == 'q' || key == 'Q')break;}}if (pApp->error != KS_OK)outputErr(ksError, "Error while receiving data!");// 清理内核层DLL中分配的资源ksError = KS_execKernelFunctionEx(pApp->hKernel, // 内核句柄"_exitFunction", // 内核层退出函数KS_INVALID_HANDLE, // 传参KS_INVALID_HANDLE, // 上下文KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {outputErr(ksError, "KS_execKernelFunctionEx", "Error while deallocating resources on kernel level!");KS_closeDriver();return;}//------------------------------------------------------------------------------------------------------------// 使用共享句柄卸载内核DLL。// 尽管KS_closeDriver()释放了所有分配的资源(如共享内存和加载的内核),// 明确释放您分配的资源是很好的习惯。//------------------------------------------------------------------------------------------------------------// 释放内核ksError = KS_freeKernel(pApp->hKernel); // 内核句柄if (ksError != KS_OK)outputErr(ksError, "KS_freeKernel", "Unable to unload the kernel!");// 清理共享内存ksError = KS_freeSharedMemEx(hSharedMemory, // 共享内存句柄KSF_NO_FLAGS); // 无标记if (ksError != KS_OK)outputErr(ksError, "KS_freeSharedMemEx", "Unable to remove shared memory!");// 关闭设备,清理所有资源ksError = KS_closeDriver();if (ksError != KS_OK)outputErr(ksError, "KS_closeDriver", "Unable to close the driver!");waitTime(500 * ms);outputTxt(" ");outputTxt("End of program 'EtherCATBasics'.");
}
EtherCATBasics_dll.cpp
/* Copyright (c) 2011-2024 by Kithara Software GmbH. All rights reserved. *///##############################################################################################################
//
// 文件: EtherCATBasics_dll.cpp
//
// 使用模块: EtherCAT模块, 时钟模块, 网络模块, 实时模块
//
// 用户应用程序和内核DLL之间的共享定义,例如:如何通过EtherCAT实现基本进程数据交换的示例
//
// 开发者: m.gru 2011-05-11
//
//##############################################################################################################/*=====================================================================*\| *** 免责声明 *** || || 本代码仅是示例程序,您可以随意使用,我们不承担任何法律责任! || |\*=====================================================================*//##############################################################################################################
//
// 目的:
//
// 该示例说明了如何在不考虑同步读取进程数据的情况下实现基本的进程数据交换。
// 此外,该示例还展示了如何检测从站可用的分布式时钟功能以及如何利用这些功能。
// 首先,我们打开网络作为 EtherCAT 主站,并创建一个 EtherCAT 主站。
// 然后,我们让用户从连接的 EtherCAT 从站中选择一个,并将其映射到创建的数据集。
// 如果所选的 EtherCAT 从站支持分布式时钟 (DC) 功能,用户可以选择其中一种支持的模式。
// 然后,用户必须从从站的一个 PDO 中选择一个变量。
// 将创建一个定时器,触发拓扑(已连接的 EtherCAT从站)与本示例实例化的 EtherCAT 主站之间的数据交换。
// 所选变量每 100ms 显示一次(定时器每 1ms 接收一次)。
//
//##############################################################################################################//--------------------------------------------------------------------------------------------------------------
// 为了在主程序和内核 DLL 之间共享数据结构定义,我们使用一个公共头文件。
//--------------------------------------------------------------------------------------------------------------#include "EtherCATBasics.h"// 共享内存用于在内核层 DLL 和用户层的应用程序之间共享数据。
SharedData* _pSys = NULL;// 指向从站选定 ECAT 设备变量接收到的数据。
void* _pSlaveData;// 在数据集中要查看的变量的位置和长度。
int _bitOffset;
int _bitLength;// 前置声明:定时器回调、数据集回调,声明在文件末尾定义的回调函数。
KSError __stdcall timerCallBack (void* /*pArgs*/, void* /*pContext*/);
KSError __stdcall dataSetCallBack(void* /*pArgs*/, void* /*pContext*/);//--------------------------------------------------------------------------------------------------------------
// 这是初始化函数。
// 它在加载内核后被调用,并将共享内存的句柄作为参数传递。
//
// 注意!请记住,所有函数都应声明为 'extern "C"'!
// 否则它们的名字可能无法被加载器找到。
// 必须通过 '__declspec(dllexport)' 导出它们。
//--------------------------------------------------------------------------------------------------------------extern "C" KSError __declspec(dllexport) __stdcall _initFunction(void* pArgs, void* /*pContext*/) {KSError ksError;// 共享内存的指针通过 KS_execKernelFunctionEx() 作为 'pArgs' 参数传递。_pSys = (SharedData*)pArgs;//------------------------------------------------------------------------------------------------------------// 在这里我们等待主站连接到拓扑。// 代替轮询主站状态,您也可以注册一个回调来处理此事件。// 详情请参阅手册中的 KS_installEcatHandler()。//------------------------------------------------------------------------------------------------------------_pSys->masterState.structSize = sizeof(KSEcatMasterState); // 不要忘记初始化 structSize!for (int i = 0; i < 50; ++i) {ksError = KS_queryEcatMasterState(_pSys->hMaster, // 主站句柄&_pSys->masterState, // 返回KSEcatMasterState 结构体KSF_NO_FLAGS); // 无标志if (ksError != KS_OK)return ksError;if (_pSys->masterState.connected)break;KS_microDelay(100 * ms);}if (_pSys->masterState.connected == 0)return KSERROR_CATEGORY_ETHERCAT;//------------------------------------------------------------------------------------------------------------// 为了准备过程数据交换,我们需要创建一个数据集并分配一个同步对象给它。// 因为我们稍后会使用 KS_getEcatDataObjAddress() 来获取内存位置。//------------------------------------------------------------------------------------------------------------ksError = KS_createEcatDataSet(_pSys->hMaster, // 主站句柄&_pSys->hDataSet, // 写入新数据集句柄的地址NULL, // 数据集数据的应用程序空间指针(未使用)NULL, // 数据集数据的内核空间指针(未使用)KSF_NO_FLAGS); // 无标记if (ksError != KS_OK)return ksError;//------------------------------------------------------------------------------------------------------------// 创建一个回调,当数据集从从站返回数据时调用。// 安装数据集处理器允许我们在从从站接收到数据时作出反应。//------------------------------------------------------------------------------------------------------------ksError = KS_createCallBack(&_pSys->hDataSetCallBack, // 写入新回调句柄的地址dataSetCallBack, // 回调函数NULL, // 回调参数(未使用)KSF_DIRECT_EXEC, // 标志,这里内核级别0); // 优先级(内核级别未使用)if (ksError != KS_OK)return ksError;// 安装创建的回调作为数据集处理器。ksError = KS_installEcatHandler(_pSys->hDataSet, // 数据集句柄KS_DATASET_SIGNAL, // 事件代码_pSys->hDataSetCallBack, // 回调句柄KSF_NO_FLAGS); // 无标志if (ksError != KS_OK)return ksError;// 创建一个定时器回调,该回调将定期执行并将数据集发送到拓扑。ksError = KS_createCallBack(&_pSys->hTimerCallBack, // 写入新回调句柄的地址timerCallBack, // 回调函数NULL, // 回调参数(未使用)KSF_DIRECT_EXEC, // 标志,这里内核级别0); // 优先级(内核级别未使用)if (ksError != KS_OK)return ksError;// 创建一个周期为 1 毫秒的定时器,并分配回调给它。ksError = KS_createTimer(&_pSys->hTimer, // 写入新定时器句柄的地址1 * ms, // 定时器周期(100纳秒单位)_pSys->hTimerCallBack, // 回调句柄KSF_REALTIME_EXEC | // 精确的高分辨率实时定时器KSF_DONT_START); // 不立即启动if (ksError != KS_OK)return ksError;return KS_OK;
}//--------------------------------------------------------------------------------------------------------------
// 这是清理函数,关闭 EtherCAT 主站和网络设备,并移除定时器、其回调、数据集、数据集处理器、数据集回调和 EtherCAT 从站。
//--------------------------------------------------------------------------------------------------------------extern "C" KSError __declspec(dllexport) __stdcall _exitFunction(void* /*pArgs*/, void* /*pContext*/) {if (_pSys == NULL) // 共享内存未映射!return KSERROR_FUNCTION_NOT_AVAILABLE; // _initFunction 未调用?KSError ksError;// 关闭ksError = KS_changeEcatState(_pSys->hDataSet, // 数据集句柄KS_ECAT_STATE_SAFEOP, // 状态KSF_NO_FLAGS); // 无标志if (ksError != KS_OK)return ksError;// 停止定时器。ksError = KS_stopTimer(_pSys->hTimer); // 定时器句柄if (ksError != KS_OK)return ksError;// 切换 SAFEOPksError = KS_changeEcatState(_pSys->hDataSet, // DataSet 句柄KS_ECAT_STATE_INIT, // 状态KSF_NO_FLAGS); // 无标志if (ksError != KS_OK)return ksError;// 移除定时器ksError = KS_removeTimer(_pSys->hTimer); // 定时器句柄if (ksError != KS_OK)return ksError;// 移除定时器回调。ksError = KS_removeCallBack(_pSys->hTimerCallBack); // 定时器回调句柄if (ksError != KS_OK)return ksError;// 卸载数据集处理器ksError = KS_installEcatHandler(_pSys->hDataSet, // DataSet 句柄KS_DATASET_SIGNAL, // 回调事件类型KS_INVALID_HANDLE, // 使事件无效KSF_NO_FLAGS); // 无标志if (ksError != KS_OK)return ksError;// 移除数据集回调ksError = KS_removeCallBack(_pSys->hDataSetCallBack); // DataSet 回调句柄if (ksError != KS_OK)return ksError;// 删除数据集ksError = KS_deleteEcatDataSet(_pSys->hDataSet); // DataSet 句柄if (ksError != KS_OK)return ksError;// 切换 OP 状态与 EtherCAT 从站ksError = KS_changeEcatState(_pSys->hSlave, // 从站句柄KS_ECAT_STATE_INIT, // 状态类型KSF_NO_FLAGS); // DataSet if (ksError != KS_OK)return ksError;// EtherCAT 主站的状态应在结束时更改为 'init'ksError = KS_changeEcatState(_pSys->hMaster, // 主站句柄KS_ECAT_STATE_INIT, // 状态类型KSF_NO_FLAGS); // 无标志if (ksError != KS_OK)return ksError;//------------------------------------------------------------------------------------------------------------// 删除EtherCAT从站//------------------------------------------------------------------------------------------------------------ksError = KS_deleteEcatSlave(_pSys->hSlave); // 从站句柄if (ksError != KS_OK)return ksError;//------------------------------------------------------------------------------------------------------------// 关闭EtherCAT主站//------------------------------------------------------------------------------------------------------------ksError = KS_closeEcatMaster(_pSys->hMaster); // 主站句柄if (ksError != KS_OK)return ksError;//------------------------------------------------------------------------------------------------------------// 关闭网络适配器//------------------------------------------------------------------------------------------------------------ksError = KS_closeNetwork(_pSys->hAdapter, // 网络适配器句柄KSF_NO_FLAGS); // 无标志if (ksError != KS_OK)return ksError;return KS_OK;
}//--------------------------------------------------------------------------------------------------------------
// _startDataExchange()函数将被用户调用,以开始数据交换。
//--------------------------------------------------------------------------------------------------------------extern "C" __declspec(dllexport) KSError __stdcall _startDataExchange(void* /*pArgs*/, void* /*pContext*/) {KSError ksError;_pSys->error = KS_OK;// 查询所选变量的地址、位偏移量和位长度。ksError = KS_getEcatDataObjAddress(_pSys->hDataSet, // DataSet句柄_pSys->hSlave, // 从站句柄_pSys->varIndex, // 索引号_pSys->varSubIndex, // 子索引号NULL, // 应用程序空间数据指针(未使用)&_pSlaveData, // 返回从站数据&_bitOffset, // 返回位偏移量&_bitLength, // 返回位长度KSF_NO_FLAGS); // 无标志if (ksError != KS_OK)return ksError;//------------------------------------------------------------------------------------------------------------// 如果 EtherCAT 从站具有分布式时钟(Distributed Clocks)功能,我们就需要启用该功能,以确保该示例能按预期运行。// 在进入 SAFEOP 之前必须调用 KS_activateEcatDcMode(),它将启动相关的定时器。//------------------------------------------------------------------------------------------------------------int64ref dcStartTime = 0;ksError = KS_activateEcatDcMode(_pSys->hDataSet, // 主站, 从站或DataSet 句柄dcStartTime, // startTime,在不久的将来的一段时间为01000000, // 循环时间(ns)->1 ms0, // 偏移时间(ns)_pSys->hTimer, // 定时器句柄KSF_NO_FLAGS); // 无标记if (ksError != KS_OK && KSERROR_CODE(ksError) != KSERROR_FEATURE_NOT_LICENSED)return ksError;// 为了读取过程数据,必须将EtherCAT从站的状态更改为“KS_ECAT_STATE_SAFEOP”。ksError = KS_changeEcatState(_pSys->hDataSet, // DataSet 句柄KS_ECAT_STATE_SAFEOP, // 切换状态KSF_NO_FLAGS); // 无标记if (ksError != KS_OK)return ksError;// 现在将Slave切换到OP。ksError = KS_changeEcatState(_pSys->hDataSet, // DataSet 句柄KS_ECAT_STATE_OP, // 切换状态KSF_NO_FLAGS); // 无标记if (ksError != KS_OK)return ksError;return KS_OK;
}// 该回调由定时器定期调用,并通过 KS_postEcatDataSet()启动进程数据交换。
KSError __stdcall timerCallBack(void* /*pArgs*/, void* /*pContext*/) {KSError ksError;if (_pSys->error != KS_OK)return _pSys->error;// 下发数据ksError = KS_postEcatDataSet(_pSys->hDataSet, // DataSet 句柄KSF_NO_FLAGS); // 无标记return _pSys->error = ksError;
}//--------------------------------------------------------------------------------------------------------------
// 当 DataSet 从从站拓扑返回主站时,将调用此回调。
// 从站将向其中写入进程数据。
// KS_readEcatDataSet() 用于检索 DataSet 以访问数据。
//--------------------------------------------------------------------------------------------------------------KSError __stdcall dataSetCallBack(void* /*pArgs*/, void* /*pContext*/) {KSError ksError;static bool running = false;ksError = KS_readEcatDataSet(_pSys->hDataSet, // DataSet 句柄KSF_NO_FLAGS); // 无标记if (ksError != KS_OK) {if ((running == 0) && (KSERROR_CODE(ksError) == KSERROR_NO_RESPONSE))// 该错误表示从站设备没有回答 KS_postEcatDataSet()。// 有些从站设备需要更多时间才能完全进入 SAFEOP 并应答 DataSet。// 因此,我们在此忽略这个错误。// 实际应用中的应用程序应该在启动和运行阶段以不同方式处理这个错误。return KS_OK;_pSys->error = ksError;return ksError;}//------------------------------------------------------------------------------------------------------------// 一旦 KS_readEcatDataSet() 成功返回数据,就说明从站程序已进入 SAFEOP 或更高版本。// 从现在起,我们将认真处理每一个错误。//------------------------------------------------------------------------------------------------------------running = true;//------------------------------------------------------------------------------------------------------------// 根据bitLength获取数据,然后通过bitOffset向右移位。//------------------------------------------------------------------------------------------------------------if (_bitLength > 0 && _bitLength <= 8)_pSys->data = *reinterpret_cast<byte*>(_pSlaveData) >> _bitOffset;if (_bitLength > 8 && _bitLength <= 16)_pSys->data = *reinterpret_cast<ushort*>(_pSlaveData) >> _bitOffset;if (_bitLength > 24 && _bitLength <= 32)_pSys->data = *reinterpret_cast<uint*>(_pSlaveData) >> _bitOffset;//------------------------------------------------------------------------------------------------------------// 获取想要的数据,并将结果存储在共享内存中,供应用程序访问。//------------------------------------------------------------------------------------------------------------if (_bitLength < 32)_pSys->data &= (1 << _bitLength) - 1;_pSys->error = ksError;return ksError;
}//--------------------------------------------------------------------------------------------------------------
// 需要实现 DllMain 函数,该函数在 DLL 加载时不会被执行。
//
// 对于初始化,请定义一个特殊的 init 函数,并在调用 KS_loadKernel()时将其名称作为参数传递给它,
// 或者在加载内核的句柄以后在加载的 DLL 调用函数(如本例所示)时使用,
// 请不要在加载内核时执行的 init 函数,而是在加载内核后自己明确地调用它,并根据需要传递参数,如本例所示。
//--------------------------------------------------------------------------------------------------------------#define WIN32_LEAN_AND_MEAN
#pragma pack(push, 8)
#include <windows.h>
#pragma pack(pop)BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID pReserved) {return TRUE;
}
相关文章:

【EtherCATBasics】- KRTS C++示例精讲(2)
EtherCATBasics示例讲解 目录 EtherCATBasics示例讲解结构说明代码讲解 项目打开请查看【BaseFunction精讲】。 结构说明 EtherCATBasics:应用层程序,主要用于人机交互、数据显示、内核层数据交互等; EtherCATBasics.h : 数据定义…...
MYSQL无法被连接问题
如果您在尝试连接到MySQL服务器时遇到问题,以下描述了您可以采取的一些措施来纠正该问题。 确保服务器正在运行。如果没有,则客户端无法连接到它。例如,如果尝试连接到服务器失败并出现以下消息之一,则可能是服务器未运行…...
【Python】什么是字典(Dictionary)?
什么是字典(Dictionary)? 字典(Dictionary)是 Python 中一种 可变(mutable)的数据结构,用于存储键值对(key-value pairs)。字典通过 键(key&…...

Web安全 - API 成批分配漏洞的四种修复方案
文章目录 概述危害修复建议与实施方案解决方案 1:手动绑定数据解决方案 2:使用 DTO 进行数据过滤解决方案 3:启用字段白名单解决方案 4:验证输入数据模式 验证修复有效性小结 概述 批量分配漏洞(Mass Assignment&#…...

计算机网络实验室建设方案
一、计算机网络实验室拓扑结构 计算机网络综合实验室解决方案,是面向高校网络相关专业开展教学实训的综合实训基地解决方案。教学实训系统采用 B/S架构,通过公有云教学实训平台在线学习模式,轻松实现网络系统建设与运维技术的教学…...
ubuntu20.04 调试bcache源码
搭建单步调试bcache的环境,/dev/sdb作为backing dev, /dev/sdc作为cache dev。 一、宿主机环境 1)安装ubuntu 20.04 : 参考ubuntu20.04 搭建kernel调试环境第一篇--安装系统_ubuntu kernel-CSDN博客安装,其中的第六…...
xss csrf怎么预防?
一、XSS(跨站脚本攻击)预防 XSS 是指攻击者向目标网站注入恶意脚本,从而在用户浏览器中执行。 1. 输入过滤 清理用户输入: 拦截或清理HTML特殊字符(如 <, >, , ", &)。使用安全库&#x…...
near-synonym反义词生成(2):Prompt +Bert-MLM(FT)
near-synonym之反义词生成方法二 near-synonym, 中文反义词/近义词/同义词(antonym/synonym)工具包. 方法一为(neg_antonym): Word2vec -> ANN -> NLI -> Length 方法二为(mlm_antonym): Prompt Bert-MLM(FT) Beam-Search 项目地址 github: https://github.com/yon…...

【服务器项目部署】⭐️将本地项目部署到服务器!
目录 🍸前言 🍻一、服务器选择 🍹 二、服务器环境部署 2.1 java 环境部署 2.2 mysql 环境部署 🍸三、项目部署 3.1 静态页面调整 3.2 服务器端口开放 3.3 项目部署 🍹四、测试 🍸前言 小伙伴们大家好…...

Neo4j Desktop无法打开
解决方案——断网 9号——周一早上就开始打不开,后面去他官网找解决方案,看他们老外解决方法都是重新安装,但是都没彻底解决,我试着重新下载安装桌面版:1.6.1版本,9号晚上7.30的时候经过两次重新安装可以打…...
【编程语言】Kotlin快速入门 - 泛型
Kotlin的泛型与Java十分类似,也是使用这种的语法结构: class Fruit<T> {fun eat(f : T): Unit {println("eat...")}fun <T> buy(p : T): T {return p} }泛型限制 和Java一样,Kotlin也允许泛型是某个类的子类 fun &l…...
【PostgreSQL】入门篇——在不同操作系统上安装 PostgreSQL
PostgreSQL在 Windows、macOS 和 Linux(以 Ubuntu 为例)的安装步骤,以及可能出现的问题和解决办法。 一、在 Windows 上安装 PostgreSQL 1. 下载 PostgreSQL 安装程序 访问 PostgreSQL 官方网站:PostgreSQL Downloads点击“Dow…...

【Docker】部署MySQL容器
关于docker,Windows上使用Powershell/CMD执行指令,Linux系统直接使用终端执行指令。 拉取MySQL 也可以跳过拉取步骤,直接run,这样本地容器不存在的话,会自动拉取最新/指定的版本。 # 默认拉取最新版本 docker pull …...

mysql9.0windows安装
第一步下载 官网地址:https://dev.mysql.com/downloads/mysql/ 点击后,选择不登录下载 第二步安装 双击下载的msi文件进行安装。打开后页面如下,选择安装类型,选择自定义安装。点击Next下一步。 自行选择安装目录 选好后点击…...

word中文献引用[]符号的上下标格式修改
word中文献引用[]符号的上下标格式修改 百度网址 1、查找打开使用通配符,输入[[][0-9]{1,2}[]],即可匹配所有的字[1],[12]这些字符,然后鼠标点击替换为的空白处,再点击特殊格式–>“字体”,选中上标,最…...

计算机毕设-基于springboot的游戏创意工坊与推广平台的设计与实现(附源码+lw+ppt+开题报告)
博主介绍:✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围:Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…...

kafka的备份策略:从备份到恢复
文章目录 一、全量备份二、增量备份三、全量恢复四、增量恢复 前言:Kafka的备份的单元是partition,也就是每个partition都都会有leader partiton和follow partiton。其中leader partition是用来进行和producer进行写交互,follow从leader副本进…...

【畅购商城】微信支付之支付回调和支付状态
目录 Nuxt.js IP 启动 支付回调 回调接口 后端实现 查看支付状态 后端实现 前端实现 前置技术:RabbitMQ 更新订单状态 Nuxt.js IP 启动 "config": {"nuxt": {"host": "0.0…...

【Compose multiplatform教程18】多平台资源的设置和配置
要正确配置项目以使用多平台资源,请执行以下操作: 添加库依赖项。 为每种资源创建必要的目录。 为限定资源创建其他目录(例如,深色 UI 主题或本地化字符串的不同图像)。 依赖项和目录设置 要访问多平台项目中的资源…...

MT6765核心板_MTK6765安卓核心板规格参数_联发科MTK模块开发
MTK6765安卓核心板是基于联发科高效八核处理器平台开发的一款强大硬件解决方案。这款核心板的核心是采用12纳米工艺打造的MTK6765 CPU,具备四个主频高达2.3GHz的CORTEX-A53核心和四个主频为1.8GHz的CORTEX-A53核心,提供了卓越的处理性能。用户可以根据需…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
用递归算法解锁「子集」问题 —— LeetCode 78题解析
文章目录 一、题目介绍二、递归思路详解:从决策树开始理解三、解法一:二叉决策树 DFS四、解法二:组合式回溯写法(推荐)五、解法对比 递归算法是编程中一种非常强大且常见的思想,它能够优雅地解决很多复杂的…...

rm视觉学习1-自瞄部分
首先先感谢中南大学的开源,提供了很全面的思路,减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接:https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架: 代码框架结构:readme有…...