#include "Publisher.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "WeighingDDSTypePubSubTypes.hpp" #include "msg.hpp" #include "MsgHandler.hpp" #include "DEBUG.hpp" using namespace eprosima::fastdds::dds; PublisherApp::PublisherApp( const int& domain_id) : factory_(nullptr) , participant_(nullptr) , publisher_(nullptr) , topic_(nullptr) , writer_(nullptr) , type_(new WeighingSystem::ReadCardRspPubSubType()) , matched_(0) , samples_sent_(0) , stop_(false) , queue_trigger_publisher_(nullptr) , queue_trigger_topic_(nullptr) , queue_trigger_writer_(nullptr) , queue_trigger_type_(new Queue::QueueTriggerPubSubType()) , current_date_() , sequence_num_(0) , last_card_() , current_message_id_() , wait_end_time_(std::chrono::steady_clock::now() - std::chrono::seconds(10)) // 初始化为过去时间,启动时不等待 , last_trigger_card_() , wait_active_(false) // 启动后不处于等待期,由 OnCardDetected 激活 { // Create the participant DomainParticipantQos pqos = PARTICIPANT_QOS_DEFAULT; pqos.name("ReadCard_pub_participant"); pqos.wire_protocol().builtin.discovery_config.leaseDuration = Duration_t(60, 0); pqos.wire_protocol().builtin.discovery_config.leaseDuration_announcementperiod = Duration_t(30, 0); factory_ = DomainParticipantFactory::get_shared_instance(); participant_ = factory_->create_participant(domain_id, pqos, nullptr, StatusMask::none()); if (participant_ == nullptr) { throw std::runtime_error("ReadCard Participant initialization failed"); } // Register ReadCardRsp type type_.register_type(participant_); // Create the publisher (for ReadCardRsp) PublisherQos pub_qos = PUBLISHER_QOS_DEFAULT; participant_->get_default_publisher_qos(pub_qos); publisher_ = participant_->create_publisher(pub_qos, nullptr, StatusMask::none()); if (publisher_ == nullptr) { throw std::runtime_error("ReadCard Publisher initialization failed"); } // Create the topic (ReadCardRsp) TopicQos topic_qos = TOPIC_QOS_DEFAULT; participant_->get_default_topic_qos(topic_qos); topic_ = participant_->create_topic("ReadCardRsp", type_.get_type_name(), topic_qos); if (topic_ == nullptr) { throw std::runtime_error("ReadCard Topic initialization failed"); } // Create the data writer (ReadCardRsp) DataWriterQos writer_qos = DATAWRITER_QOS_DEFAULT; publisher_->get_default_datawriter_qos(writer_qos); writer_qos.reliability().kind = ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; writer_qos.durability().kind = DurabilityQosPolicyKind::VOLATILE_DURABILITY_QOS; writer_qos.history().kind = HistoryQosPolicyKind::KEEP_LAST_HISTORY_QOS; writer_ = publisher_->create_datawriter(topic_, writer_qos, this, StatusMask::all()); if (writer_ == nullptr) { throw std::runtime_error("ReadCard DataWriter initialization failed"); } // ── 创建 QueueTrigger 发布者 ── queue_trigger_type_.register_type(participant_); PublisherQos qt_pub_qos = PUBLISHER_QOS_DEFAULT; participant_->get_default_publisher_qos(qt_pub_qos); queue_trigger_publisher_ = participant_->create_publisher(qt_pub_qos, nullptr, StatusMask::none()); TopicQos qt_topic_qos = TOPIC_QOS_DEFAULT; participant_->get_default_topic_qos(qt_topic_qos); queue_trigger_topic_ = participant_->create_topic( "QueueTrigger", queue_trigger_type_.get_type_name(), qt_topic_qos); DataWriterQos qt_writer_qos = DATAWRITER_QOS_DEFAULT; queue_trigger_publisher_->get_default_datawriter_qos(qt_writer_qos); qt_writer_qos.reliability().kind = RELIABLE_RELIABILITY_QOS; qt_writer_qos.durability().kind = VOLATILE_DURABILITY_QOS; qt_writer_qos.history().kind = KEEP_LAST_HISTORY_QOS; queue_trigger_writer_ = queue_trigger_publisher_->create_datawriter( queue_trigger_topic_, qt_writer_qos, this, StatusMask::all()); } PublisherApp::~PublisherApp() { if (nullptr != participant_) { participant_->delete_contained_entities(); factory_->delete_participant(participant_); } } void PublisherApp::on_publication_matched( DataWriter* writer, const PublicationMatchedStatus& info) { if (info.current_count_change == 1) { { std::lock_guard lock(mutex_); matched_ = info.current_count; } DEBUG(writer->get_topic()->get_name() << " Publisher matched."); cv_.notify_one(); } else if (info.current_count_change == -1) { { std::lock_guard lock(mutex_); matched_ = info.current_count; } DEBUG(writer->get_topic()->get_name() << " Publisher unmatched."); } } void PublisherApp::run(std::shared_ptr handler) { // ── 拉卡检测回调:替代方案下放给主循环处理,这里不再绑定 ── // 真正的触发逻辑在 run() 主循环里: // HandleDeviceMsg → 把卡号写入 handler->pending_trigger_card_ // 主循环检测字段非空 → PublishQueueTrigger + PauseAutoFind + 启动 3s 等待期 // Device->Core 的响应(DDS 载荷:index + map>) WeighingSystem::ReadCardRsp rsp; while (!is_stopped()) { if (handler->isOpen() == true) { // ── 处理设备消息(FindCard 响应会更新 m_CardId 并写入 pending_trigger_card_) ── if (handler->HandleDeviceMsg(rsp)) { // 当前版本不主动读卡,设备消息主要是 FindCard 响应 rsp.msg().clear(); } // ── 检测新卡:发 QueueTrigger + 启动 3s 等待期 ── std::string cardToTrigger; { std::lock_guard lock(handler->trigger_mtx_); if (!handler->pending_trigger_card_.empty()) { cardToTrigger = handler->pending_trigger_card_; handler->pending_trigger_card_.clear(); // 消费掉 } } if (!cardToTrigger.empty()) { // 防止同一张卡短时间内重复触发(3 秒等待期内忽略) auto now = std::chrono::steady_clock::now(); if (now < wait_end_time_ && cardToTrigger == last_trigger_card_) { DEBUG("Card " << cardToTrigger << " still in 3s wait period, skipping QueueTrigger"); } else { PublishQueueTrigger(cardToTrigger); // 暂停自动寻卡(state 切到 AUTO_FIND,m_AutoFind=false), // 阻止 3 秒等待期内 HandleDdsMsg 接收 read/write handler->PauseAutoFind(); wait_end_time_ = now + std::chrono::seconds(10); wait_active_ = true; // 标记等待期,主循环在 3s 后才调 ForceReset last_trigger_card_ = cardToTrigger; DEBUG("QueueTrigger sent for card=" << cardToTrigger << ", waiting 10s before resuming auto-find"); } } // ── 等待期检查 ── auto now2 = std::chrono::steady_clock::now(); if (now2 >= wait_end_time_) { // 等待期刚结束:仅在这一次调用时恢复 AUTO_FIND if (wait_active_) { static auto last_resume_log = std::chrono::steady_clock::time_point{}; if (last_resume_log.time_since_epoch().count() == 0 || now2 - last_resume_log > std::chrono::seconds(60)) { DEBUG("10s wait elapsed, resuming AUTO_FIND"); last_resume_log = now2; } handler->ForceResetToAutoFind(); wait_active_ = false; } // pending read response(来自超时跳过) if (handler->HasPendingReadResponse()) { auto pendingRsp = handler->GetPendingReadResponse(); if (!pendingRsp.empty()) { rsp.msg()["read"] = pendingRsp; rsp.index(handler->GetCurrentIndex()); DEBUG("Publishing pending read response (from timeout skip)"); writer_->write(&rsp); rsp.msg().clear(); } } handler->AutoFindTick(); } else { // ── 等待期内:不发 DDS、不触发读卡、不恢复自动寻卡 ── auto remaining = std::chrono::duration_cast( wait_end_time_ - now2).count(); } } std::unique_lock period_lock(mutex_); cv_.wait_for(period_lock, std::chrono::milliseconds(10), [this]() { return is_stopped(); }); } } bool PublisherApp::is_stopped() { return stop_.load(); } void PublisherApp::on_data_available(DataReader* /*reader*/) { // 不再订阅任何 DDS Topic,无需处理 } void PublisherApp::on_subscription_matched( DataReader* /*reader*/, const SubscriptionMatchedStatus& /*info*/) { // 不再订阅任何 DDS Topic,无需处理 } void PublisherApp::stop() { stop_.store(true); cv_.notify_one(); } std::string PublisherApp::GenerateMessageId(const std::string& card_number) { std::lock_guard lock(id_mtx_); time_t now = time(nullptr); char date_buf[9]; strftime(date_buf, sizeof(date_buf), "%Y%m%d", localtime(&now)); std::string today(date_buf); // 日期变了:重置序号,清空上次的 card 和 message_id if (today != current_date_) { current_date_ = today; sequence_num_ = 0; last_card_.clear(); current_message_id_.clear(); } // 同一天、相同卡号:复用上次的 message_id if (!last_card_.empty() && card_number == last_card_ && !current_message_id_.empty()) { DEBUG("GenerateMessageId: card unchanged (" << card_number << "), reuse " << current_message_id_); return current_message_id_; } // 不同卡号:序号累加 sequence_num_++; last_card_ = card_number; char id[32]; snprintf(id, sizeof(id), "qm-%s-%06u", today.c_str(), sequence_num_); current_message_id_ = std::string(id); DEBUG("GenerateMessageId: new card=" << card_number << " -> " << current_message_id_); return current_message_id_; } void PublisherApp::PublishQueueTrigger(const std::string& card_number) { Queue::QueueTrigger msg; const std::string mid = GenerateMessageId(card_number); msg.message_id(mid); msg.card_number(card_number); msg.device_id("3"); // 本机读卡器设备编号 msg.trigger_time(""); // 暂时为 null(空字符串) queue_trigger_writer_->write(&msg); DEBUG("Published QueueTrigger: message_id=" << mid << " card=" << card_number << " device_id=3"); }