FastDDS服务发现之EDP的收发

目录

  • EDP发送
    • DataWrtier
  • EDP接收

EDP对象的创建在FastDDS服务发现之PDP和EDP的创建中有详细介绍,PDP的收发在FastDDS服务发现之PDP和EDP的收发中有详细介绍,本文主要分析Simple EDP报文的发送和消息接收。

EDP发送

在Fast DDS中,Writer和Reader是通信的端点,他们通过topic和data type进行交互。一个writer会发布一个特定topic的数据,而reader则会订阅这个topic的数据。

当EDP开始时,每个participant都会公布其reader和writer的信息(包括topic和data type),并接收其他participant的reader和writer的信息。然后EDP会比较这些信息,如果一个writer的topic和data type与一个reader的topic和data type相匹配,那么这个writer和reader就会被匹配起来,它们就可以进行数据通信。

SEDP的发现阶段是在创建DataWriter和DataReader时进行的。

DataWrtier

DataWrtier对象完成创建之后,开始EDP的发现过程,具体调用流程如下:
PublisherImpl::create_datawriter —— DataWriterImpl::enable() —— RTPSParticipantImpl::registerWriter() —— BuiltinProtocols::addLocalWriterEDP::newLocalWriterProxyData
《TODO: EDP发送时序图》

EDP::newLocalWriterProxyData函数开始代码分析

bool EDP::newLocalWriterProxyData(RTPSWriter* writer,const TopicAttributes& att,const WriterQos& wqos)
{auto init_fun = [this, writer, &att, &wqos](WriterProxyData* wpd,bool updating,const ParticipantProxyData& participant_data){const NetworkFactory& network = mp_RTPSParticipant->network_factory();const auto& watt = writer->getAttributes();wpd->guid(writer->getGuid());wpd->key() = wpd->guid();if (watt.multicastLocatorList.empty() && watt.unicastLocatorList.empty()){wpd->set_locators(participant_data.default_locators);}else{wpd->set_multicast_locators(watt.multicastLocatorList, network);wpd->set_announced_unicast_locators(watt.unicastLocatorList);fastdds::rtps::network::external_locators::add_external_locators(*wpd,watt.external_unicast_locators);}wpd->RTPSParticipantKey() = mp_RTPSParticipant->getGuid();wpd->topicName(att.getTopicName());wpd->typeName(att.getTopicDataType());wpd->topicKind(att.getTopicKind());if (att.type_id.m_type_identifier._d() != static_cast<uint8_t>(0x00)){wpd->type_id(att.type_id);}if (att.type.m_type_object._d() != static_cast<uint8_t>(0x00)){wpd->type(att.type);}if (att.type_information.assigned()){wpd->type_information(att.type_information);}wpd->typeMaxSerialized(writer->getTypeMaxSerialized());wpd->m_qos.setQos(wqos, true);wpd->userDefinedId(watt.getUserDefinedID());wpd->persistence_guid(watt.persistence_guid);
#if HAVE_SECURITYif (mp_RTPSParticipant->is_secure()){wpd->security_attributes_ = watt.security_attributes().mask();wpd->plugin_security_attributes_ = watt.security_attributes().plugin_endpoint_attributes;}else{wpd->security_attributes_ = 0UL;wpd->plugin_security_attributes_ = 0UL;}
#endif // if HAVE_SECURITYif (att.auto_fill_type_information){// TypeInformation, TypeObject and TypeIdentifierif (!att.type_information.assigned()){const types::TypeInformation* type_info =types::TypeObjectFactory::get_instance()->get_type_information(wpd->typeName().c_str());if (type_info != nullptr){wpd->type_information() = *type_info;}}}if (att.auto_fill_type_object){bool has_type_id = true;if (att.type_id.m_type_identifier._d() == static_cast<uint8_t>(0x00)){has_type_id = false;const types::TypeIdentifier* type_id =types::TypeObjectFactory::get_instance()->get_type_identifier_trying_complete(wpd->typeName().c_str());if (type_id != nullptr){has_type_id = true;wpd->type_id().m_type_identifier = *type_id;}}if (att.type.m_type_object._d() == static_cast<uint8_t>(0x00)){bool type_is_complete = has_type_id &&wpd->type_id().m_type_identifier._d() == types::EK_COMPLETE;const types::TypeObject* type_obj =types::TypeObjectFactory::get_instance()->get_type_object(wpd->typeName().c_str(), type_is_complete);if (type_obj != nullptr){wpd->type().m_type_object = *type_obj;}}}return true;};//ADD IT TO THE LIST OF READERPROXYDATAGUID_t participant_guid;WriterProxyData* writer_data = this->mp_PDP->addWriterProxyData(writer->getGuid(), participant_guid, init_fun);if (writer_data == nullptr){return false;}#ifdef FASTDDS_STATISTICS// notify monitor service about the new local entity proxyif (nullptr != this->mp_PDP->get_proxy_observer()){this->mp_PDP->get_proxy_observer()->on_local_entity_change(writer_data->guid(), true);}
#endif //FASTDDS_STATISTICS//PAIRINGif (this->mp_PDP->getRTPSParticipant()->should_match_local_endpoints()){pairing_writer_proxy_with_any_local_reader(participant_guid, writer_data);} pairingWriter(writer, participant_guid, *writer_data);//DO SOME PROCESSING DEPENDING ON THE IMPLEMENTATION (SIMPLE OR STATIC)processLocalWriterProxyData(writer, writer_data);return true;
}

这段代码分为五部分进行分析。

  1. 调用PDP::addWriterProxyData创建或找到一个WriterProxyData对象。PDP发现的participant的信息会存放在participant_proxies_中,所以先从participant_proxies_中查找PDP发现的participant,EDP阶段发现的对端的RTPSWriter和RTPSReader会存放到ParticipantProxyDataProxyHashTable<WriterProxyData>* m_writersProxyHashTable<ReaderProxyData>* m_readers中,所以会优先查找是否已经将writers/reader存放到ProxyHashTable<WriterProxyData>* m_writersProxyHashTable<ReaderProxyData>* m_readers中。如果有则直接使用,如果没有则构造一个WriterProxyData对象,再调用EDP::newLocalWriterProxyData中的lambda表达式init_fun进行初始化。这里的初始化是把当前writer的信息赋给WriterProxyData对象,包括writer的guid,topic name,data type,qos等,这些信息都是需要通过EDP发送给其他已经通过PDP发现了的participant端用于进行EDP的匹配的。所以,保存在ParticipantProxyData中的ProxyHashTable<ReaderProxyData>用于将当前的endpooint和EDP监听收到对端的Endpoint进行对比匹配使用。
WriterProxyData* PDP::addWriterProxyData(const GUID_t& writer_guid,GUID_t& participant_guid,std::function<bool(WriterProxyData*, bool, const ParticipantProxyData&)> initializer_func)
{EPROSIMA_LOG_INFO(RTPS_PDP, "Adding writer proxy data " << writer_guid);WriterProxyData* ret_val = nullptr;// notify statistics modulegetRTPSParticipant()->on_entity_discovery(writer_guid, ParameterPropertyList_t());std::lock_guard<std::recursive_mutex> guardPDP(*this->mp_mutex);for (ParticipantProxyData* pit : participant_proxies_){if (pit->m_guid.guidPrefix == writer_guid.guidPrefix){// Copy participant data to be used outside.participant_guid = pit->m_guid;// Check that it is not already there:auto wpi = pit->m_writers->find(writer_guid.entityId);if (wpi != pit->m_writers->end()){ret_val = wpi->second;if (!initializer_func(ret_val, true, *pit)){return nullptr;}RTPSParticipantListener* listener = mp_RTPSParticipant->getListener();if (listener){WriterDiscoveryInfo info(*ret_val);info.status = WriterDiscoveryInfo::CHANGED_QOS_WRITER;listener->onWriterDiscovery(mp_RTPSParticipant->getUserRTPSParticipant(), std::move(info));check_and_notify_type_discovery(listener, *ret_val);}return ret_val;}// Try to take one entry from the poolif (writer_proxies_pool_.empty()){size_t max_proxies = writer_proxies_pool_.max_size();if (writer_proxies_number_ < max_proxies){// Pool is empty but limit has not been reached, so we create a new entry.++writer_proxies_number_;ret_val = new WriterProxyData(mp_RTPSParticipant->getAttributes().allocation.locators.max_unicast_locators,mp_RTPSParticipant->getAttributes().allocation.locators.max_multicast_locators,mp_RTPSParticipant->getAttributes().allocation.data_limits);}else{EPROSIMA_LOG_WARNING(RTPS_PDP, "Maximum number of writer proxies (" << max_proxies <<") reached for participant " << mp_RTPSParticipant->getGuid() << std::endl);return nullptr;}}else{// Pool is not empty, use entry from poolret_val = writer_proxies_pool_.back();writer_proxies_pool_.pop_back();}// Copy network configuration from participant to writer proxyret_val->networkConfiguration(pit->m_networkConfiguration);// Add to ParticipantProxyData(*pit->m_writers)[writer_guid.entityId] = ret_val;if (!initializer_func(ret_val, false, *pit)){return nullptr;}RTPSParticipantListener* listener = mp_RTPSParticipant->getListener();if (listener){WriterDiscoveryInfo info(*ret_val);info.status = WriterDiscoveryInfo::DISCOVERED_WRITER;listener->onWriterDiscovery(mp_RTPSParticipant->getUserRTPSParticipant(), std::move(info));check_and_notify_type_discovery(listener, *ret_val);}return ret_val;}}return nullptr;
}
  1. 查找当前participant中是否有匹配的reader。这一步在EDP::pairing_writer_proxy_with_any_local_reader中实现
bool EDP::pairing_writer_proxy_with_any_local_reader(const GUID_t& participant_guid,WriterProxyData* wdata)
{(void)participant_guid;EPROSIMA_LOG_INFO(RTPS_EDP, wdata->guid() << " in topic: \"" << wdata->topicName() << "\"");mp_RTPSParticipant->forEachUserReader([&, wdata](RTPSReader& r) -> bool{auto temp_reader_proxy_data = get_temporary_reader_proxies_pool().get();GUID_t readerGUID = r.getGuid();if (mp_PDP->lookupReaderProxyData(readerGUID, *temp_reader_proxy_data)){MatchingFailureMask no_match_reason;fastdds::dds::PolicyMask incompatible_qos;bool valid = valid_matching(temp_reader_proxy_data.get(), wdata, no_match_reason, incompatible_qos);const GUID_t& writer_guid = wdata->guid();temp_reader_proxy_data.reset();if (valid){
#if HAVE_SECURITYif (!mp_RTPSParticipant->security_manager().discovered_writer(readerGUID, participant_guid,*wdata, r.getAttributes().security_attributes())){EPROSIMA_LOG_ERROR(RTPS_EDP, "Security manager returns an error for reader " << readerGUID);}
#elseif (r.matched_writer_add(*wdata)){EPROSIMA_LOG_INFO(RTPS_EDP_MATCH,"WP:" << wdata->guid() << " match R:" << r.getGuid() << ". WLoc:" <<wdata->remote_locators());//MATCHED AND ADDED CORRECTLY:if (r.getListener() != nullptr){MatchingInfo info;info.status = MATCHED_MATCHING;info.remoteEndpointGuid = writer_guid;r.getListener()->onReaderMatched(&r, info);const SubscriptionMatchedStatus& sub_info =update_subscription_matched_status(readerGUID, writer_guid, 1);r.getListener()->onReaderMatched(&r, sub_info);}}
#endif // if HAVE_SECURITY}else{if (no_match_reason.test(MatchingFailureMask::incompatible_qos) && r.getListener() != nullptr){r.getListener()->on_requested_incompatible_qos(&r, incompatible_qos);}if (r.matched_writer_is_matched(writer_guid)&& r.matched_writer_remove(writer_guid)){
#if HAVE_SECURITYmp_RTPSParticipant->security_manager().remove_writer(readerGUID, participant_guid,writer_guid);
#endif // if HAVE_SECURITY//MATCHED AND ADDED CORRECTLY:if (r.getListener() != nullptr){MatchingInfo info;info.status = REMOVED_MATCHING;info.remoteEndpointGuid = writer_guid;r.getListener()->onReaderMatched(&r, info);const SubscriptionMatchedStatus& sub_info =update_subscription_matched_status(readerGUID, writer_guid, -1);r.getListener()->onReaderMatched(&r, sub_info);}}}}// keep lookingreturn true;});return true;
}
  1. 查找ParticipantProxyData中是否有匹配的reader,在EDP::pairingWriter中实现
bool EDP::pairingWriter(RTPSWriter* W,const GUID_t& participant_guid,const WriterProxyData& wdata)
{(void)participant_guid;EPROSIMA_LOG_INFO(RTPS_EDP, W->getGuid() << " in topic: \"" << wdata.topicName() << "\"");std::lock_guard<std::recursive_mutex> pguard(*mp_PDP->getMutex());ResourceLimitedVector<ParticipantProxyData*>::const_iterator pit = mp_PDP->ParticipantProxiesBegin();if (!this->mp_PDP->getRTPSParticipant()->should_match_local_endpoints()){pit++;}for (; pit != mp_PDP->ParticipantProxiesEnd(); ++pit){for (auto& pair : *(*pit)->m_readers){ReaderProxyData* rdatait = pair.second;const GUID_t& reader_guid = rdatait->guid();if (reader_guid == c_Guid_Unknown){continue;}MatchingFailureMask no_match_reason;fastdds::dds::PolicyMask incompatible_qos;bool valid = valid_matching(&wdata, rdatait, no_match_reason, incompatible_qos);if (valid){
#if HAVE_SECURITYif (!mp_RTPSParticipant->security_manager().discovered_reader(W->getGuid(), (*pit)->m_guid,*rdatait, W->getAttributes().security_attributes())){EPROSIMA_LOG_ERROR(RTPS_EDP, "Security manager returns an error for writer " << W->getGuid());}
#elseif (W->matched_reader_add(*rdatait)){EPROSIMA_LOG_INFO(RTPS_EDP_MATCH,"RP:" << rdatait->guid() << " match W:" << W->getGuid() << ". WLoc:" <<rdatait->remote_locators());//MATCHED AND ADDED CORRECTLY:if (W->getListener() != nullptr){MatchingInfo info;info.status = MATCHED_MATCHING;info.remoteEndpointGuid = reader_guid;W->getListener()->onWriterMatched(W, info);const GUID_t& writer_guid = W->getGuid();const PublicationMatchedStatus& pub_info =update_publication_matched_status(reader_guid, writer_guid, 1);W->getListener()->onWriterMatched(W, pub_info);}}
#endif // if HAVE_SECURITY}else{if (no_match_reason.test(MatchingFailureMask::incompatible_qos) && W->getListener() != nullptr){W->getListener()->on_offered_incompatible_qos(W, incompatible_qos);}//EPROSIMA_LOG_INFO(RTPS_EDP,RTPS_CYAN<<"Valid Matching to writerProxy: "<<wdatait->m_guid<<RTPS_DEF<<endl);if (W->matched_reader_is_matched(reader_guid) && W->matched_reader_remove(reader_guid)){
#if HAVE_SECURITYmp_RTPSParticipant->security_manager().remove_reader(W->getGuid(), participant_guid, reader_guid);
#endif // if HAVE_SECURITY//MATCHED AND ADDED CORRECTLY:if (W->getListener() != nullptr){MatchingInfo info;info.status = REMOVED_MATCHING;info.remoteEndpointGuid = reader_guid;W->getListener()->onWriterMatched(W, info);const GUID_t& writer_guid = W->getGuid();const PublicationMatchedStatus& pub_info =update_publication_matched_status(reader_guid, writer_guid, -1);W->getListener()->onWriterMatched(W, pub_info);}}}}}return true;
}
  1. 组装EDP报文并发送
bool EDPSimple::processLocalWriterProxyData(RTPSWriter* local_writer,WriterProxyData* wdata)
{EPROSIMA_LOG_INFO(RTPS_EDP, wdata->guid().entityId);(void)local_writer;auto* writer = &publications_writer_;#if HAVE_SECURITYif (local_writer->getAttributes().security_attributes().is_discovery_protected){writer = &publications_secure_writer_;}
#endif // if HAVE_SECURITYCacheChange_t* change = nullptr;bool ret_val = serialize_writer_proxy_data(*wdata, *writer, true, &change);if (change != nullptr){writer->second->add_change(change);}return ret_val;
}

EDP接收

EDP对象的创建阶段,会创建两个EDP的listener对象:EDPSimplePUBListener和EDPSimpleSUBListener,用于监听EDP endpoints,所以收到EDP报文后,会回调到EDPSimplePUBListener::onNewCacheChangeAdded中:

void EDPSimpleSUBListener::onNewCacheChangeAdded(RTPSReader* reader,const CacheChange_t* const change_in)
{CacheChange_t* change = (CacheChange_t*)change_in;//std::lock_guard<std::recursive_mutex> guard(*this->sedp_->subscriptions_reader_.first->getMutex());EPROSIMA_LOG_INFO(RTPS_EDP, "");if (!computeKey(change)){EPROSIMA_LOG_WARNING(RTPS_EDP, "Received change with no Key");}ReaderHistory* reader_history =
#if HAVE_SECURITYreader == sedp_->subscriptions_secure_reader_.first ?sedp_->subscriptions_secure_reader_.second :
#endif // if HAVE_SECURITYsedp_->subscriptions_reader_.second;if (change->kind == ALIVE){PREVENT_PDP_DEADLOCK(reader, change, sedp_->mp_PDP);// Note: change is removed from history inside this method.add_reader_from_change(reader, reader_history, change, sedp_);}else{//REMOVE WRITER FROM OUR READERS:EPROSIMA_LOG_INFO(RTPS_EDP, "Disposed Remote Reader, removing...");GUID_t reader_guid = iHandle2GUID(change->instanceHandle);//Removing change from historyreader_history->remove_change(change);reader->getMutex().unlock();this->sedp_->mp_PDP->removeReaderProxyData(reader_guid);reader->getMutex().lock();}
}

这个函数中在判断change->kind == ALIVE之后核心实现为调用EDPBaseSUBListener::add_reader_from_change:

void EDPBaseSUBListener::add_reader_from_change(RTPSReader* reader,ReaderHistory* reader_history,CacheChange_t* change,EDP* edp,bool release_change /*=true*/)
{//LOAD INFORMATION IN TEMPORAL WRITER PROXY DATAconst NetworkFactory& network = edp->mp_RTPSParticipant->network_factory();CDRMessage_t tempMsg(change->serializedPayload);auto temp_reader_data = edp->get_temporary_reader_proxies_pool().get();if (temp_reader_data->readFromCDRMessage(&tempMsg, network,edp->mp_RTPSParticipant->has_shm_transport(), true, change->vendor_id)){if (temp_reader_data->guid().guidPrefix == edp->mp_RTPSParticipant->getGuid().guidPrefix){EPROSIMA_LOG_INFO(RTPS_EDP, "From own RTPSParticipant, ignoring");return;}auto copy_data_fun = [&temp_reader_data, &network](ReaderProxyData* data,bool updating,const ParticipantProxyData& participant_data){if (!temp_reader_data->has_locators()){temp_reader_data->set_remote_locators(participant_data.default_locators, network, true);}if (updating && !data->is_update_allowed(*temp_reader_data)){EPROSIMA_LOG_WARNING(RTPS_EDP,"Received incompatible update for ReaderQos. reader_guid = " << data->guid());}*data = *temp_reader_data;return true;};//LOOK IF IS AN UPDATED INFORMATIONGUID_t participant_guid;ReaderProxyData* reader_data =edp->mp_PDP->addReaderProxyData(temp_reader_data->guid(), participant_guid, copy_data_fun);// Release the temporary proxytemp_reader_data.reset();// Remove change from history.reader_history->remove_change(reader_history->find_change(change), release_change);// At this point we can release reader lock, cause change is not usedreader->getMutex().unlock();if (reader_data != nullptr) //ADDED NEW DATA{edp->pairing_reader_proxy_with_any_local_writer(participant_guid, reader_data);}else{EPROSIMA_LOG_WARNING(RTPS_EDP, "From UNKNOWN RTPSParticipant, removing");}// Take again the reader lock.reader->getMutex().lock();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/469115.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【计网】数据链路层笔记

【计网】数据链路层 数据链路层概述 数据链路层在网络体系结构中所处的地位 链路、数据链路和帧 链路(Link)是指从一个节点到相邻节点的一段物理线路(有线或无线)&#xff0c;而中间没有任何其他的交换节点。 数据链路(Data Link)是基于链路的。当在一条链路上传送数据时&a…

重学SpringBoot3-整合 Elasticsearch 8.x (二)使用Repository

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 整合 Elasticsearch 8.x &#xff08;二&#xff09;使用Repository 1. 环境准备1.1 项目依赖1.2 Elasticsearch 配置 2. 使用Repository的基本步骤2.1 创建实体类2.2 创…

计算机课程管理:Spring Boot与工程认证的协同创新

3系统分析 3.1可行性分析 通过对本基于工程教育认证的计算机课程管理平台实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本基于工程教育认证的计算机课程管理平…

<项目代码>YOLOv8 苹果腐烂识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

游戏引擎学习第四天

视频参考:https://www.bilibili.com/video/BV1aDmqYnEnc/ BitBlt 是 Windows GDI&#xff08;图形设备接口&#xff09;中的一个函数&#xff0c;用于在设备上下文&#xff08;device context, DC&#xff09;之间复制位图数据。BitBlt 的主要用途是将一个图像区域从一个地方复…

SPIRE: Semantic Prompt-Driven Image Restoration 论文阅读笔记

这是一篇港科大学生在google research 实习期间发在ECCV2024的语义引导生成式修复的文章&#xff0c;港科大陈启峰也挂了名字。从首页图看效果确实很惊艳&#xff0c;尤其是第三行能用文本调控修复结果牌上的字。不过看起来更倾向于生成&#xff0c;对原图内容并不是很复原&…

如何平滑切换Containerd数据目录

如何平滑切换Containerd数据目录 大家好&#xff0c;我是秋意零。 这是工作中遇到的一个问题。搭建的服务平台&#xff0c;在使用的过程中频繁出现镜像本地拉取不到问题&#xff08;在项目群聊中老是被人出来&#x1f605;&#xff09;原因是由于/目录空间不足导致&#xff0…

Sharding运行模式、元数据、持久化详解

运行模式 单机模式 能够将数据源和规则等元数据信息持久化&#xff0c;但无法将元数据同步至多个Sharding实例&#xff0c;无法在集群环境中相互感知。 通过某一实例更新元数据之后&#xff0c;会导致其他实例由于获取不到最新的元数据而产生不一致的错误。 适用于工程师在本…

基于springboot+小程序的鲜花管理系统(鲜花1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 本网上花店微信小程序分为管理员还有用户两个权限&#xff0c;管理员可以管理用户的基本信息内容&#xff0c;可以管理公告信息以及鲜花信息&#xff0c;能够与用户进行相互交流等操作&am…

金融学期末速成笔记

【拯救者】金融学速成&#xff08;基础习题&#xff09; 重点: 市场经济是发达的商品经济。在市场经济条件下&#xff0c;市场机制作为资源配置方式&#xff0c;发挥基础性作用。 除具有商品经济的一般特征外&#xff0c;与商品经济相比&#xff0c;市场经济还具有一些新的特征…

后悔没早点知道,Coze 插件 + Cursor 原来可以这样赚钱

最近智能体定制化赛道异常火爆。 打开闲鱼搜索"Coze 定制",密密麻麻的服务报价直接刷屏,即使表明看起来几十块的商家,一细聊,都是几百到上千不等的报价。 有趣的是,这些智能体定制化服务背后,最核心的不只是工作流设计,还有一个被很多人忽视的重要角色 —— …

嵌入式采集网关(golang版本)

为了一次编写到处运行&#xff0c;使用纯GO编写&#xff0c;排除CGO&#xff0c;解决在嵌入式中交叉编译难问题 硬件设备&#xff1a;移远EC200A-CN LTE Cat 4 无线通信模块&#xff0c;搭载openwrt操作系统&#xff0c;90M内存

基于Multisim数字电子秒表0-60S电路(含仿真和报告)

【全套资料.zip】数字电子秒表电路Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.秒表最大计时值为60秒&#xff1b; 2. 2位数码管显示&#xff0c;分辨率为1秒&#xff1b; 3.具有清零…

昇思大模型平台打卡体验活动:项目2基于MindSpore通过GPT实现情感分类

昇思大模型平台打卡体验活动&#xff1a;项目2基于MindSpore通过GPT实现情感分类 1. 载入与处理数据集 在情感分类任务中&#xff0c;我们使用了IMDB数据集&#xff0c;首先需要对数据进行加载和处理。由于原数据集没有验证集&#xff0c;我们将训练集重新划分为训练集和验证…

Mac如何实现最简单的随时监测实时运行状态的方法

Mac book有着不同于Windows的设计逻辑与交互设计&#xff0c;使得Mac book有着非常棒的使用体验&#xff0c;但是在Mac电脑的使用时间过长时&#xff0c;电脑也会出现响应速度变慢或应用程序崩溃的情况&#xff0c;当发生的时候却不知道什么原因导致的&#xff0c;想要查询电脑…

有趣的Midjourney作品赏析(附提示词)

中文提示词&#xff1a;国风少年 C4D软件,高分辨率,超细节,超现实主义, 英文提示词&#xff1a;National Style Youth Cinema4D,high resolution,hyper detailed,surrealism, --niji 6 --ar 1:1 中文提示词&#xff1a;粘土模型&#xff0c;男性穿着中世纪欧洲蓝色盔甲&#x…

时序预测 | gamma伽马模型锂电池寿命预测 EM算法粒子滤波算法结合参数估计

时序预测 | gamma伽马模型锂电池寿命预测 EM算法粒子滤波算法结合参数估计 目录 时序预测 | gamma伽马模型锂电池寿命预测 EM算法粒子滤波算法结合参数估计预测效果基本介绍参考资料 预测效果 基本介绍 gamma伽马模型锂电池寿命预测 EM算法粒子滤波算法结合参数估计 伽马模型、…

男同事36岁,听说被裁拿了12万。今天看到他退了群,但下午领导就反悔了,让他回来,还要把12万补偿退回来

亲爱的读者们&#xff0c;今天咱们来聊聊职场那些事儿。你听说过吗&#xff1f;有位男同事&#xff0c;36岁&#xff0c;被裁了&#xff0c;拿了12万补偿金&#xff0c;然后退了群。你以为这就是结局&#xff1f;不&#xff0c;故事才刚刚开始&#xff01; 想象一下&#xff0…

李佳琦回到巅峰背后,双11成直播电商分水岭

时间倏忽而过&#xff0c;又一年的双11即将宣告结束。 从双11正式开始前的《新所有女生的offer》&#xff0c;到被作为“比价”标杆被其他平台直播间蹭、被与其他渠道品牌比较&#xff0c;再到直播间运营一时手快多发了红包……整个双11周期下来&#xff0c;李佳琦直播间在刷新…

Golang | Leetcode Golang题解之第546题移除盒子

题目&#xff1a; 题解&#xff1a; func removeBoxes(boxes []int) int {dp : [100][100][100]int{}var calculatePoints func(boxes []int, l, r, k int) intcalculatePoints func(boxes []int, l, r, k int) int {if l > r {return 0}if dp[l][r][k] 0 {r1, k1 : r, k…