【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 : 数据定义…...
AI 简史:从神经元到现代大模型
AI 简史:从神经元到现代大模型 人工智能 (AI) 和深度学习 (DL) 在过去的几十年中飞速发展,推动了计算机视觉、自然语言处理和机器人等领域的进步。今年的诺贝尔物理学奖更是颁给了美国科学家约翰霍普菲尔德 (John Hopfield)和英国科学家杰弗…...
Kotlin入门到深入加强(1):基本介绍和第一个程序
吧, 一.关于本章和Kotlin 本章内容如要简单介绍一下Kotlin和创建并执行第一个Kotlin程序 什么是Kotlin,优势是什么,它和JAVA的关系又是什么 Kotlin是一种静态类型的编程语言,它运行在Java虚拟机上,并且可以与现有的Java代码无缝集成。Kotl…...
走进 Web3:探索分布式网络的未来
随着互联网的不断演变,我们正站在一个全新阶段的门槛上——Web3。它是对传统互联网(Web2)的一次深刻反思与升级,标志着一个去中心化、更加自主的数字世界的到来。Web3不仅仅是技术的革新,更代表着对互联网价值体系的根…...
信管通低代码信息管理系统应用平台
目前,国家统一要求事业单位的电脑都要进行国产化替代,替代后使用的操作系统都是基于linux的,所有以前在WINDOWS下运行的系统都不能使用了,再者,各单位的软件都很零散,没有统一起来。需要把日常办公相关的软…...
C语言从入门到放弃教程
C语言从入门到放弃 1. 介绍1.1 特点1.2 历史与发展1.3 应用领域 2. 安装2.1 编译器安装2.2 编辑器安装 3. 第一个程序1. 包含头文件2. 主函数定义3. 打印语句4. 返回值 4. 基础语法4.1 注释4.1.1 单行注释4.1.2 多行注释 4.2 关键字4.2.1 C语言标准4.2.2 C89/C90关键字…...
硬件设计:RS232电平标准
RS232是一种常用的串行通信接口标准,主要用于计算机和外部设备之间的数据传输。以下是RS232电平标准的详细介绍: 1. 电气特性: 信号电平: 逻辑1 (MARK):-3V到-15V之间。通常,-5V到-15V之间为有效的逻辑1。…...
前端初学基础
一.Web开发 前端三件 HTML ,页面展现 CSS,样式 JS(JavaScript),动起来 二,HTML 1.HTML概念 网页,网站中的一个页面,网页是构成网站的基本元素,是承载各种网站应用的平台。通俗的说,网站就…...
在 CentOS 7 上安装 Node.js 20 并升级 GCC、make 和 glibc
在 CentOS 7 上安装 Node.js 20 并升级 GCC、make 和 glibc 📖 前言 在 CentOS 7 上使用 NVM 安装 Node.js 后,可能会遇到如下问题: node: /lib64/libm.so.6: version GLIBC_2.27’ not found (required by node) node: /lib64/libc.so.6:…...
mac中idea菜单工具栏没有git图标了
1.右击菜单工具栏 2.选中VCS,点击添加 3.搜索你要的工具,选中点击确定就添加了 4.回到上面一个界面,选中你要放到工具栏的工具,点击应用就好了 5.修改图标,快捷键或者右击选中编辑图标 6.选择你要的图标就好了...
C++简明教程(10)(初识类)
类的教程 C 类的完整教程 C 中,类(class)是面向对象编程的核心概念,用于定义对象的属性(数据成员)和行为(成员函数)。本教程将带你从零开始,循序渐进地学习如何定义和使…...
微机接口课设——基于Proteus和8086的打地鼠设计(8255、8253、8259)
原理图设计 汇编代码 ; I/O 端口地址定义 IOY0 EQU 0600H IOY1 EQU 0640H IOY2 EQU 0680HMY8255_A EQU IOY000H*2 ; 8255 A 口端口地址 MY8255_B EQU IOY001H*2 ; 8255 B 口端口地址 MY8255_C EQU IOY002H*2 ; 8255 C 口端口地址 MY8255_MODE EQU IOY003H*2 ; …...
ctfshow-jwt
将信息进行安全的封装,以json的方式传递 三部分分别是: Header { typ: JWT token类型 alg: HS256 算法名称 } 将这个信息进行base64加密,构成了第一部分 payload载荷,存放主要信息的地方 { "sub": 1234567890, 标准中注册的…...
植物大战僵尸融合版,真无双版,喵版,抽卡版,杂交版v3
我用夸克网盘分享了「植物大战僵尸」链接: https://pan.quark.cn/s/11a45054a4da 融合版的作者为B站up主蓝飘飘fly。该版本在原版植物基础上创新地将两种不同的植物种植在一起进行融合,创造出独特的游戏体验。抽卡版、喵版、无双版是由B站UP主【时…...
将Minio设置为Django的默认Storage(django-storages)
这里写自定义目录标题 前置说明静态文件收集静态文件 使用django-storages来使Django集成Minio安装依赖settings.py测试收集静态文件测试媒体文件 前置说明 静态文件 Django默认的Storage是本地,项目中的CSS、图片、JS都是静态文件。一般会将静态文件放到一个单独…...
MaaS(Model as a Service)
1.MasS是什么? Model as a Service(MaaS)是一种云计算服务模式,它允许用户通过互联网访问和使用机器学习模型,而不需要自行构建和维护这些模型。MaaS提供了模型的托管、管理和监控,使用户能够专注于应用程…...
Docker怎么关闭容器开机自启,批量好几个容器一起操作?
环境: WSL2 docker v25 问题描述: Docker怎么关闭容器开机自启,批量好几个容器一起操作? 解决方案: 在 Docker 中,您可以使用多种方法来关闭容器并配置它们是否在系统启动时自动启动。以下是具体步骤和…...
训练时开Dropout,推理时不开Dropout的原因以及推理过程是怎样的?(中英双语)
Dropout的概念与应用:基于Transformer模型的深入解析 在深度学习中,Dropout 是一种常用的正则化技术,主要用于防止神经网络的过拟合。在训练过程中,Dropout通过随机丢弃神经网络中的一部分神经元,降低了网络的复杂度&…...
STL heap原理和用法
在C STL(标准模板库)中,heap(堆)并不是一个独立的容器,而是一组基于容器(通常是 vector)实现的算法函数,用于将一段数据组织成堆的数据结构形式,并提供了一些…...
【DRAM存储器四十三】LPDDR5介绍--寻址
👉个人主页:highman110 👉作者简介:一名硬件工程师,持续学习,不断记录,保持思考,输出干货内容 参考资料:《某LPDDR5数据手册》 、《JESD209-5C》 前面我们介绍的LPDDR5一个die的数据位宽...
mac启ssh服务用于快速文件传输
x.1 在mac上启SSH服务 方法一:图形交互界面启ssh(推荐) 通过sharing - advanced - remote login来启动ssh;(中文版mac应该是 “系统设置 → 通用 → 共享”里打开“远程登录”来启动) 查看自己的用户名和…...
Java设计模式 —— 【结构型模式】享元模式(Flyweight Pattern) 详解
文章目录 概述结构案例实现优缺点及使用场景 概述 享元模式也叫蝇量模式:运用共享技术有效地支持大量细粒度的对象; 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象…...
前端最新Vue2+Vue3基础入门到实战项目全套教程,自学前端vue就选黑马程序员,一套全通关!
Vue 快速上手 Vue概念 Vue 是一个用于构建用户界面的渐进式框架 构建用户界面:基于数据渲染出用户看到的页面 渐进式:循序渐进 框架:一套完整的项目解决方案 Vue 的两种使用方式: ① Vue 核心包开发 场景:局部 模块改造 ② Vue 核心包 &am…...
肿瘤电场治疗费用
肿瘤电场治疗作为一种前沿的肿瘤治疗方法,近年来备受关注。该方法通过利用特定频率的交流电场,作用于恶性肿瘤细胞,以达到抑制肿瘤生长的目的。然而,随着这种治疗方法的普及,其费用问题也逐渐成为患者和家属关注的焦点…...
datatables快速入门
官网 进入官网https://datatables.net/ 点击下载 支持多种方式下载 快速入门 这里以cdn的方式演示 https://cdn.datatables.net/ <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport&…...
ReactPress 1.6.0:重塑博客体验,引领内容创新
ReactPress 是一个基于Next.js的博客&CMS系统, Github项目地址:https://github.com/fecommunity/reactpress 欢迎Star。 体验地址:http://blog.gaoredu.com/ 今天,我们自豪地宣布ReactPress 1.6.0版本的正式发布,…...
我的创作纪念日(五年)
慕然回首 平平无奇的周一早晨,收到来自csdn的提醒,创作纪念日五周年了,这也意味着我从事开发行业差不多有整整五年了,五年啊!你知道这五年我是怎么过的吗?一句Just do IT,我做it整整做了五年&am…...
企业AI助理背后的技术架构:从数据到智能决策
在当今数字化时代,企业AI助理已经成为推动企业数字化转型和智能化升级的重要工具。它们通过整合企业内外部数据资源,运用先进的算法和模型,为企业提供高效、精准的智能决策支持。本文将深入探讨企业AI助理背后的技术架构,从数据收…...
主流AI视频生成工具|Sora零基础入门指南
Sora是什么? Sora 是 OpenAI 推出的新一代 AI 视频生成工具。它能让用户通过简单的文本描述或图片提示,快速生成高质量的视频内容。无论是广告短片、创意视频,还是实验性艺术作品,Sora 都能帮助创作者以极低的门槛实现自己的想法。…...
单元测试/系统测试/集成测试知识总结
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 一、单元测试的概念 单元测试是对软件基本组成单元进行的测试,如函数或一个类的方法。当然这里的基本单元不仅仅指的是一个函数或者方法࿰…...
前端文件下载多方式集合
基于vueelement UI框架 // ts-ignore import axios from "axios"; import { ElMessage } from "element-plus"; import webConfig from "/config";class FileDownload {/*** 文件流下载* param url string 下载地址* param params object 请求…...
PG备份恢复--pg_dump
pg_dump pg_dump 是一个逻辑备份工具。使用 pg_dump 可以在数据库处于使用状态下进行一致 性的备份,它不会阻塞其他用户对数据库的访问 。 一致性备份是 pg_dump 开始运行时,给数据库打了一个快照,且在 pg_dump 运行过程 中发生的更新将不会被备份。 …...
Java进阶学习笔记|面向对象
第一章.类和对象 1.面向对象的介绍 1.面向过程:自己的事情自己干,代表语言C语言洗衣服:每一步自己要亲力亲为 -> 找个盆,放点水,找个搓衣板,搓搓搓 2.面向对象:自己的事情别人帮忙去干,代表语言Java语言 洗衣服:自己的事情别人干 -> 全自动洗衣机3.为啥要使用面向对…...
ESP32_H2(IDF)学习系列-ADC模数转换(连续转换)
一、简介(节选手册) 资料参考https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32h2/api-reference/peripherals/adc_calibration.html 1 概述 ESP32-H2 搭载了以下模拟外设: • 一个 12 位逐次逼近型模拟数字转换器 (SAR ADC)&…...
二叉树的右视图
199. 二叉树的右视图 已解答 中等 相关标签 相关企业 给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 # Definition for a binary tree node. # class TreeNode(object): #…...
Pinpoint 是一个开源的分布式追踪系统
pinpointagent2.2.2.tar 是 Pinpoint 的一个版本,Pinpoint 是一个开源的分布式追踪系统,专门用于对 Java 应用程序进行性能监控、日志记录和故障诊断。它可以帮助开发人员和运维人员追踪和分析微服务架构中服务之间的调用链,并进行性能分析。…...
React 脚手架使用指南
React 脚手架使用指南 目录 概述创建项目项目结构常用命令配置说明最佳实践 概述 什么是 React 脚手架? React 脚手架(Create React App)是 Facebook 官方提供的创建 React 单页应用的工具。它提供了一个零配置的现代构建设置。 为什么使用脚手架?…...
Win10提示“缺少fbgemm.dll”怎么办?缺失fbgemm.dll文件的修复方法来啦!
fbgemm.dll文件的作用 fbgemm.dll(Facebook GEMM library)是一个动态链接库文件,它主要用于优化矩阵乘法运算,提高计算性能。虽然它不是Windows 10系统的核心组件,但在某些应用程序或游戏中,尤其是那些需要…...
vue2 elementui if导致的rules判断失效
优化目标 和 目标转化出价必填的 切换的时候还会隐藏掉 这时候的if语句会导致rules判断失效 我的办法是把判断拉到外面 别放在el-form-item里 <section v-if"unitForm.baseTarget OCPM && unitForm.cpaTargetOptions ! undefined && unitForm.cpaTa…...
基于科大讯飞大模型Spark 4.0 Ultar 的微信聊天机器人搭建教程---零基础搭建最详细图文版!!!
1、软件的下载及部署 搭建该微信聊天机器人,主要依赖的编程语言为python,故需要搭建python的开发环境,主要包含python解释器的下载与安装以及编译器pycharm的下载与安装 1.1 python解释器的下载与安装 python解释器官网:https:…...
SpringBoot整合Netty
前言 Netty是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高并发协议服务器和客户端。 Netty主要基于Java NIO实现,提供了异步和事件驱动的网络编程工具,简化了TCP和UDP服务器的编程。 Netty广泛应用于分布式系统、实时通信、游戏开发等领域,例如,…...
机器学习之PCA降维
主成分分析(PCA,Principal Component Analysis) 主成分分析(PCA)是一种常见的无监督学习技术,广泛应用于数据降维、数据可视化以及特征提取等任务。PCA的目标是通过线性变换将数据从高维空间映射到低维空间…...
《 OpenCV 环境搭建》
《OpenCV 环境搭建》 一、OpenCV 简介二、准备工作三、安装 OpenCV(一)获取安装包(二)安装过程 四、配置环境变量(一)系统环境变量配置(二)项目环境变量配置(以 Visual S…...
【阅读记录-章节7】Build a Large Language Model (From Scratch)
系列文章目录 【阅读记录-章节1】Build a Large Language Model (From Scratch) 【阅读记录-章节2】Build a Large Language Model (From Scratch) 【阅读记录-章节3】Build a Large Language Model (From Scratch) 【阅读记录-章节4】Build a Large Language Model (From Scr…...
在js中实现下载base64数据,兼容低版本
详细步骤如下 1、解析 Base64 数据: 如果数据流中包含前缀 data:…;base64,,先分离 MIME 类型和 Base64 数据部分。如果没有前缀,假设默认 MIME 类型(如 application/octet-stream)。 2、Base64 解码: 使…...
垃圾分割数据集labelme格式659张1类别
数据集格式:labelme格式(不包含mask文件,仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数):659 标注数量(json文件个数):659 标注类别数:1 标注类别名称:["garbage"] 每个类别标注的框数&#…...
深入探索 npm cache clean --force:清理 npm 缓存的艺术
npm 是 JavaScript 编程语言的包管理器,它是 Node.js 运行环境的默认包管理器。npm 提供了一个丰富的生态系统,包括数以百万计的可重用代码包。然而,随着时间的推移,npm 的缓存可能会变得庞大,影响性能或导致一些奇怪的…...
Flink中并行度和slot的关系——任务和任务槽
一、任务槽(task slots) Flink的每一个TaskManager是一个JVM进程,在其上可以运行多个线程(任务task),那么每个线程可以拥有多少进程资源呢?任务槽就是这样一个概念,对taskManager上每个任务运行…...
22【AUTOSAR自适应平台设计的概述01】杂项概念介绍
1.AUTOSAR自适应平台设计的概述 本小课题主要是让读者对AUTOSAR自适应平台设计的思路有个宏观的概念,不拘泥于具体的技术细节。 总结如下: 概述AUTOSAR自适应平台(AP)的设计。为AP用户和实施者提供总体设计和关键概念2.具体的章节由以下几个部分组成: 技术范围和方法: 介…...
基础运维学习计划-base版
目录 需要学习的内容? liunx基础 sql/mysql基础 tcp/ip协议基础 http基础 dns基础 网络基础:交换&路由概念&原理 常见网络协议 月学习计划 12.26 日 (bilibili自己找视频看看,资源很多) 12.27日 1…...