You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

337 lines
11 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*!
* @file Publisher.cxx
* This file contains the implementation of the publisher functions.
*
* This file was generated by the tool fastddsgen.
*/
#include "Publisher.hpp"
#include <condition_variable>
#include <csignal>
#include <stdexcept>
#include <thread>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/log/Log.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/publisher/qos/DataWriterQos.hpp>
#include <fastdds/dds/publisher/qos/PublisherQos.hpp>
#include "SystemPubSubTypes.hpp"
#include "msg.hpp"
#include "MsgHandler.hpp"
#include "WeightStabilityDetector.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::ScaleInfoPubSubType())
, matched_(0)
, samples_sent_(0)
, stop_(false)
{
//
// Create the participant
DomainParticipantQos pqos = PARTICIPANT_QOS_DEFAULT;
pqos.name("Weigh_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("WeighingSystem::ScaleInfo Participant initialization failed");
}
// Register the type
type_.register_type(participant_);
// Create the publisher
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("WeighingSystem::ScaleInfo Publisher initialization failed");
}
// Create the topic
TopicQos topic_qos = TOPIC_QOS_DEFAULT;
participant_->get_default_topic_qos(topic_qos);
topic_ = participant_->create_topic("ScaleInfo", type_.get_type_name(), topic_qos);
if (topic_ == nullptr)
{
throw std::runtime_error("WeighingSystem::ScaleInfo Topic initialization failed");
}
// Create the data writer
DataWriterQos writer_qos = DATAWRITER_QOS_DEFAULT;
publisher_->get_default_datawriter_qos(writer_qos);
writer_qos.reliability().kind = ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS;
writer_qos.reliability().max_blocking_time = Duration_t(1, 0);
writer_qos.durability().kind = DurabilityQosPolicyKind::VOLATILE_DURABILITY_QOS;
writer_qos.history().kind = HistoryQosPolicyKind::KEEP_LAST_HISTORY_QOS;
writer_qos.history().depth = 1;
writer_qos.resource_limits().max_samples = 200;
writer_qos.resource_limits().max_instances = 1;
writer_qos.resource_limits().max_samples_per_instance = 100;
writer_qos.data_sharing().off();
writer_ = publisher_->create_datawriter(topic_, writer_qos, this, StatusMask::all());
if (writer_ == nullptr)
{
throw std::runtime_error("WeighingSystem::ScaleInfo DataWriter initialization failed");
}
}
PublisherApp::~PublisherApp()
{
if (nullptr != participant_)
{
// Delete DDS entities contained within the DomainParticipant
participant_->delete_contained_entities();
// Delete DomainParticipant
factory_->delete_participant(participant_);
}
}
void PublisherApp::on_publication_matched(
DataWriter* writer,
const PublicationMatchedStatus& info)
{
if (info.current_count_change == 1)
{
{
std::lock_guard<std::mutex> 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<std::mutex> lock(mutex_);
matched_ = info.current_count;
}
DEBUG(writer->get_topic()->get_name() << " Publisher unmatched.");
}
else
{
DEBUG(info.current_count_change << " is not a valid value for PublicationMatchedStatus current count change");
}
}
uint8_t send_time = 0;
void PublisherApp::run(std::shared_ptr<MsgHandler> handler)
{
// 配置检测器 - 使用更合理的参数
WeightStabilityDetector::Config config;
config.jitter_threshold = handler->jitter_weight; // 30kg抖动阈值
config.min_weight_threshold = handler->empty_weight; // 300kg开始检测
config.empty_car_threshold = handler->empty_weight; // 默认300kg为空车
config.fast_drop_threshold = 1000.0f; // 快速下降1吨认为车辆离开
config.required_stable_count = 24; // 20次稳定即可
config.min_jitter_count = 4; // 至少4次非增长
config.timeout_extra = 4; // 超时额外次数
config.window_size = 8; // 窗口大小8
config.max_std_dev = handler->jitter_weight; // 最大标准差30kg
config.enable_debug_log = false;
config.reset_window_on_vehicle_on = true;
config.leave_reset_delay_ms = 500; // 0.5秒后重置
// 创建检测器
WeightStabilityDetector detector(config);
// 设置状态回调
detector.setStateCallback([](WeightStabilityDetector::VehicleState state,
float weight,
[[maybe_unused]] void* user_data) {
const char* state_names[] = {
"NO_VEHICLE",
"VEHICLE_ON",
"VEHICLE_STABLE",
"VEHICLE_LEAVING"
};
send_time = 50;
std::cout << "\n=== STATE CHANGE: " << state_names[static_cast<int>(state)]
<< ", Weight: " << weight << " kg ===" << std::endl;
});
// 设置重量回调
detector.setWeightCallback([](float weight, bool is_stable, [[maybe_unused]] void* user_data) {
if (is_stable) {
// std::cout << "Weight stabilized: " << weight << " kg" << std::endl;
}
});
while (!is_stopped())
{
std::unique_lock<std::mutex> lock(MsgData::queue_cv_mtx_, std::try_to_lock);
if (lock.owns_lock())
{
if(!MsgData::ScaleCommand_queue_.empty())
{
MsgData::ScaleCommand_queue_.pop();
lock.unlock();
}
else
{
lock.unlock();
}
}
if (handler->isOpen() == true)
{
float weight;
if (handler->HandleDeviceMsg(weight) == true)
{
bool success = detector.processWeight(weight);
// ========== 调试打印1串口收到数据 ==========
//std::cout << "[DEBUG] Recv from ttyS3, weight=" << weight << std::endl;
if (!success)
{
std::cout << "Failed to process weight!" << std::endl;
continue;
}
// 获取统计信息
auto stats = detector.getStatistics();
auto state = detector.getState();
// const char *state_names[] = {
// "NO_VEHICLE", "VEHICLE_ON", "VEHICLE_STABLE", "VEHICLE_LEAVING"};
// std::cout << "State: " << state_names[static_cast<int>(state)]
// << ", Stable: " << (detector.isStable() ? "Yes" : "No")
// << ", HasVehicle: " << (detector.hasVehicle() ? "Yes" : "No") << std::endl;
// if (stats.sample_count > 0)
// {
// std::cout << "Stats: Mean=" << std::setprecision(2) << stats.mean
// << "kg, StdDev=" << stats.std_dev
// << "kg, Range=" << (stats.max - stats.min)
// << "kg, Samples=" << stats.sample_count << std::endl;
// }
// 如果稳定,显示稳定重量
WeighingSystem::ScaleInfo info;
info.State() = true;
if (weight < handler->empty_weight)
{
info.HasVehicle() = 0;
}
else
{
info.HasVehicle() = 1;
}
if (detector.isStable())
{
info.WeightOK() = true;
info.Value() = weight / 1000.0f;
info.StableValue() = weight / 1000.0f;
}
else
{
info.WeightOK() = false;
info.Value() = weight / 1000.0f;
info.StableValue() = 0;
}
if (send_time >= 50)
{
// ========== 调试打印2DDS发布前 ==========
// std::cout << "[DEBUG] DDS Publish: Value=" << info.Value()
// << " StableValue=" << info.StableValue()
// << " HasVehicle=" << info.HasVehicle() << std::endl;
writer_->write(&info);
send_time = 0;
}
}
}
if (send_time++ > 50)
{
send_time = 50;
}
// ========== 调试打印3主循环心跳 ==========
// static int heartbeat = 0;
// heartbeat++;
// if (heartbeat % 100 == 0) {
// std::cout << "[DEBUG] Main loop alive, heartbeat=" << heartbeat << std::endl;
// }
// Wait for period or stop event
std::unique_lock<std::mutex> period_lock(mutex_);
cv_.wait_for(period_lock, std::chrono::milliseconds(period_ms_), [this]()
{
return is_stopped();
});
}
}
bool PublisherApp::publish()
{
bool ret = false;
// Wait for the data endpoints discovery
std::unique_lock<std::mutex> matched_lock(mutex_);
cv_.wait(matched_lock, [&]()
{
// at least one has been discovered
return ((matched_ > 0) || is_stopped());
});
if (!is_stopped())
{
/* Initialize your structure here */
WeighingSystem::ScaleInfo sample_;
ret = (RETCODE_OK == writer_->write(&sample_));
}
return ret;
}
bool PublisherApp::is_stopped()
{
return stop_.load();
}
void PublisherApp::stop()
{
stop_.store(true);
cv_.notify_one();
}