KRTS中EtherCAT 从站热插拔

EtherCAT 从站热插拔



热插拔描述了在不关闭 EtherCAT 主站的情况下更换、添加或移除 EtherCAT 从站的功能。为了支持热插拔EtherCAT从站,KRTSEtherCAT主站提供了一些功能。最值得注意的是函数 KS_installEcatHandler,其事件为 ‘KS_TOPOLOGY_CHANGE’。

安装了带有“KS_TOPOLOGY_CHANGE”的事件回调后,上下文使用类型为 EcatTopologyUserContext,并且字段 reason 具有以下值之一:

  • KS_ECAT_TOPOLOGY_MASTER_CONNECTED - 连接到拓扑的主站

  • KS_ECAT_TOPOLOGY_MASTER_DISCONNECTED - 主设备与拓扑断开连接

  • KS_ECAT_TOPOLOGY_SLAVE_COUNT_CHANGED - 从站在线数量更改

  • KS_ECAT_TOPOLOGY_SLAVE_ONLINE - 先前创建的从站在线

  • KS_ECAT_TOPOLOGY_SLAVE_OFFLINE - 之前创建的从站离线

通过这些事件并调用 KS_enumEcatSlaves,可以监控连接到 EtherCAT 主站的从站数量和类型。热插拔 EtherCAT 从站有许多实际应用,包括检测硬件故障和/或任何类型机器的热插拔部件。对机器的热插拔部件做出反应的可能方法可能是:

假设您期望有人不时地拔下和插入一个或多个从站。然后,您将使用 ‘KS_ECAT_TOPOLOGY_SLAVE_ONLINE’ 和 ‘KS_ECAT_TOPOLOGY_SLAVE_OFFLINE’ 常量来监视这些创建的从站。

如果从服务器拓扑中拔下,您将收到一个原因为“KS_ECAT_TOPOLOGY_SLAVE_OFFLINE”的回调,该从站的句柄将在 EcatTopologyUserContext 结构中提供。调用 KS_queryEcatSlaveState 会告诉你,这个从站现在不在线。

如果该从站再次插入拓扑,您将收到一个原因为“KS_ECAT_TOPOLOGY_SLAVE_ONLINE”的回调。此时从站仍然通电,但该从站的进程数据交换已停止,因此它可能下降到状态 SAFEOP。但是,由于主站仍然拥有有关此从服务器的所有信息,因此您只需再次将该从服务器置于OP中,即可进行正常操作。

如果你期望有人关闭和打开你的从站,这将是相同的过程。唯一的区别是,从服务器将处于 INIT 状态,因为它当然是断电的。但是,主站仍然拥有有关该从站的所有信息,并且可以将其放回 OP 以进行正常操作。

结论:

EtherCAT拓扑变化可以通过不同的事件进行监控。无需停止主站,也无需重新配置从站即可恢复正常运行。更重要的是,没有必要停止过程数据交换,可能其他一些从站正在依赖它。请注意,KS_readEcatDataSet 将给出值“KSERROR_NO_RESPONSE”的错误,因为一个从站没有响应数据集。

示例

我们假设您已经知道如何通过PDO控制伺服伺服的程序,如果不清楚,您可以查看安装目录下 smp/EtherCATDataExchange等示例代码。

下面展示热拔插的关键代码:

  1. 安装拓扑回调
    // 网络拓扑结构发送改变后操作(从站热拔插)
    error = KS_createKernelCallBack(&topology_call_back_, kernel_handle_, "TopologyCallBack", nullptr,
    KSF_DIRECT_EXEC | KSF_SAVE_FPU, 0);
    if (error != KS_OK)
    {
        OutPutError("KS_createKernelCallBack DataSetCallBack  Failed", error);
        return error;
    }

    error = KS_installEcatHandler(app_shared_data_->master_handle, KS_TOPOLOGY_CHANGE, topology_call_back_, 0);
    if (error != KS_OK)
    {
        OutPutError("KS_installEcatHandler KS_TOPOLOGY_CHANGE Failed", error);
        return error;
    }
  1. 拓扑结构发生改变后,在回调中系统做相应调整,断网和断电的操作不同,这里仅展示断网情况
// EtherCAT拓扑变化回调函数
extern "C" __declspec(dllexport) KSError __stdcall TopologyCallBack(void *pArgs, void *pContext)
{
    // 应当在正转运行处理,不运行不处理
    if (kernel_data_ptr_!= nullptr && !kernel_data_ptr_->is_run_status)
    {
        return KS_OK;
    }

    KS_printK("+++++++++++ TopologyCallBack  +++++++++++++ \n");

    const auto user_context = (EcatTopologyUserContext*)pContext;

    KS_printK("--------------------------- hSlave: %d \n", user_context->hSlave);
    KS_printK("--------------------------- ctxType: 0x%X \n", user_context->ctxType);
    KS_printK("--------------------------- reason: 0x%X \n", user_context->reason);
    KS_printK("--------------------------- slavesOnline: 0x%X \n", user_context->slavesOnline);
    KS_printK("--------------------------- slavesCreatedAndOnline: 0x%X \n", user_context->slavesCreatedAndOnline);

    // 根据上下文信息,处理从站状态变化
    if (user_context->ctxType == KS_ECAT_TOPOLOGY_CHANGE)
    {
        for (int index = 0; index < AXIS_COUNT; index++)
        {
            KSEcatSlaveState slave_state;
            slave_state.structSize = sizeof(KSEcatSlaveState); // 不要忘记初始化结构大小
            KSError error = KS_enumEcatSlaves(kernel_data_ptr_->master_handle, index, &slave_state, 0);
            if (error == KS_OK)
            {
                //打印从站信息
                KS_printK("Slave %d Vendor: 0x%X\n",index, slave_state.vendor);
                KS_printK("Slave %d Product: 0x%X\n",index, slave_state.product);
                KS_printK("Slave %d State: 0x%X\n",index, slave_state.slaveState);
            }
            else
            {
                KS_printK("KS_enumEcatSlaves exec failed");
                continue;
            }
        }

        // 从站上线
        if (user_context->reason == KS_ECAT_TOPOLOGY_SLAVE_ONLINE)
        {
            KS_printK("--------------------------- ONLINE hSlave: %d \n", user_context->hSlave);

            // 查询从站状态
            KSEcatSlaveState slave_stater;
            slave_stater.structSize = sizeof(KSEcatSlaveState); // 特别注意设置结构体大小
            if (KSError error = KS_queryEcatSlaveState(user_context->hSlave, &slave_stater, 0); error == KS_OK)
            {
                KS_printK("--------------------------- Slave Stater: 0x%X \n",slave_stater.slaveState);
                // 断电会自动切换状态到 KS_ECAT_STATE_INIT
                if (slave_stater.slaveState == KS_ECAT_STATE_INIT)
                {
                    KS_printK("--------------------------- Slave KS_ECAT_STATE_INIT \n");
                    error = KS_changeEcatState(user_context->hSlave, KS_ECAT_STATE_SAFEOP, 0);
                    if (error != KS_OK)
                    {
                        KS_printK("--------------------------- Slave KS_changeEcatState KS_ECAT_STATE_SAFEOP Error");
                    }
                }

                // 断网自动切换状态到 KS_ECAT_STATE_SAFEOP
                if (slave_stater.slaveState == KS_ECAT_STATE_SAFEOP)
                {
                    KS_printK("--------------------------- Slave KS_ECAT_STATE_SAFEOP \n");
                    error = KS_changeEcatState(user_context->hSlave, KS_ECAT_STATE_OP, 0);
                    if (error != KS_OK)
                    {
                        KS_printK("--------------------------- Slave KS_changeEcatState KS_ECAT_STATE_SAFEOP Error");
                    }
                }
            }

            KSEcatMasterState master_stater;
            master_stater.structSize = sizeof(KSEcatMasterState); // 特别注意设置结构体大小
            if (KSError error = KS_queryEcatMasterState(user_context->hMaster, &master_stater, 0); error == KS_OK)
            {
                KS_printK("--------------------------- Master Stater: 0x%X \n",master_stater.masterState);

                // 断电会自动切换状态到 KS_ECAT_STATE_INIT
                if (master_stater.masterState == KS_ECAT_STATE_INIT)
                {
                    KS_printK("--------------------------- Master KS_ECAT_STATE_INIT \n");
                    error = KS_changeEcatState(user_context->hMaster, KS_ECAT_STATE_SAFEOP, 0);
                    if (error != KS_OK)
                    {
                        KS_printK("--------------------------- Master KS_changeEcatState KS_ECAT_STATE_SAFEOP Error");
                    }
                }

                // 断网自动切换状态到 KS_ECAT_STATE_SAFEOP
                if (master_stater.masterState == KS_ECAT_STATE_SAFEOP)
                {
                    KS_printK("--------------------------- Master KS_ECAT_STATE_SAFEOP \n");
                    error = KS_changeEcatState(user_context->hMaster, KS_ECAT_STATE_OP, 0);
                    if (error != KS_OK)
                    {
                        KS_printK("--------------------------- Master KS_changeEcatState KS_ECAT_STATE_SAFEOP Error");
                    }
                }
            }

            KSError error = KS_changeEcatState(kernel_data_ptr_->dataset_handle, KS_ECAT_STATE_OP, 0);
            if (error != KS_OK)
            {
                KS_printK("--------------------------- Master KS_changeEcatState KS_ECAT_STATE_SAFEOP Error");
            }
        }
        else if (user_context->reason == KS_ECAT_TOPOLOGY_SLAVE_OFFLINE) // 从站下线
        {
            KS_printK("OFFLINE hSlave: %d \n", user_context->hSlave);
        }
    }

    return KS_OK;
}

3.忽略掉线的未响应

// 数据集回调,更新完EtherCAT通信数据包后,触发
extern "C" __declspec(dllexport) KSError __stdcall DataSetCallBack(void* pArgs, void* /*pContext*/)
{
    KSError error = KS_readEcatDataSet(g_shared_data->data_set_handle, 0);
    if (error != KS_OK)
    {
        if (KSERROR_CODE(error) == KSERROR_NO_RESPONSE) // 有些从属设备需要更多时间才能完全进入 SAFEOP 并回答 DataSet
        {
            return KS_OK;
        }
        return error;  // 不可以返回 failed,否则数据集回调停止
    }
    // 执行相关操作
	//  ...

    return KS_OK;
}

PS: 注意一些资源的释放!

  • 21
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值