修改了装置重启方式,去除了外部调用的校验流程,现在直接在内部自动完成。

新增了装置升级的正式流程。
This commit is contained in:
2026-03-09 19:06:56 +08:00
parent 93def18fbd
commit f727f9ee9c
5 changed files with 556 additions and 11 deletions

View File

@@ -23,6 +23,47 @@ static uv_loop_t* global_loop = nullptr;
static uv_timer_t monitor_timer;
extern SafeMessageQueue message_queue;
// 生成与 C# CreateNormalCrc32Table 完全对应的表
static std::array<uint32_t, 256> CreateNormalCrc32Table_Char(uint32_t poly)
{
std::array<uint32_t, 256> table{};
for (int i = 0; i < 256; ++i)
{
uint32_t data = static_cast<uint32_t>(i) << 24;
for (int j = 0; j < 8; ++j)
{
bool flag = (data & 0x80000000u) == 0x80000000u;
if (flag)
{
data = (data << 1) ^ poly;
}
else
{
data <<= 1;
}
}
table[i] = data;
}
return table;
}
// 与 C# CalcNoemalCrc32 完全对应
static uint32_t CalcNormalCrc32_Char(const std::vector<unsigned char>& bytes,
uint32_t poly = 0x04C11DB7u)
{
static const std::array<uint32_t, 256> table = CreateNormalCrc32Table_Char(poly);
uint32_t crc = 0xFFFFFFFFu;
for (size_t i = 0; i < bytes.size(); ++i)
{
crc = (crc << 8) ^ table[((crc >> 24) & 0xFFu) ^ bytes[i]];
}
return crc;
}
// ClientContext 实现
ClientContext::ClientContext(uv_loop_t* loop, const DeviceInfo& device, int index)
: loop(loop), state(ConnectionState::DISCONNECTED),
@@ -348,6 +389,140 @@ void ClientContext::clear_float_cache() {
std::lock_guard<std::mutex> lock(float_cache_mutex_);
point_float_cache_.clear();
}
//升级管理相关函数
/**
* @brief 程序切片函数
* @param file 升级程序文件
* @param frame_index 要发送的帧序号
* @param frame_payload_size 单帧数据长
* @return 切片数据
*/
static std::vector<unsigned char> slice_frame_data(const std::vector<unsigned char>& file,
uint32_t frame_index,
uint32_t frame_payload_size)
{
const uint64_t offset = static_cast<uint64_t>(frame_index) * frame_payload_size;
if (offset >= file.size()) {
return {};
}
const uint64_t remain = file.size() - offset;
const uint64_t copy_len = std::min<uint64_t>(remain, frame_payload_size);
return std::vector<unsigned char>(
file.begin() + static_cast<std::ptrdiff_t>(offset),
file.begin() + static_cast<std::ptrdiff_t>(offset + copy_len));
}
/**
* @brief 装置升级参数初始化
* @param file 升级程序文件
* @param frame_payload_size 单帧数据长
* @return 是否需要升级
*/
bool ClientContext::prepare_upgrade_task(const std::vector<unsigned char>& file, uint32_t frame_payload_size)
{
if (file.empty() || frame_payload_size == 0) {
return false;
}
std::lock_guard<std::mutex> lock(upgrade_mutex_);
upgrade_task_.file_data = file;
upgrade_task_.file_size = static_cast<uint32_t>(file.size());
upgrade_task_.file_crc = CalcNormalCrc32_Char(file, 0x04C11DB7u);
upgrade_task_.frame_payload_size = frame_payload_size;
upgrade_task_.total_frames =
static_cast<uint32_t>((file.size() + frame_payload_size - 1) / frame_payload_size);
upgrade_task_.next_frame_index = 0;
upgrade_task_.active = (upgrade_task_.total_frames > 0);
return upgrade_task_.active;
}
/**
* @brief 生成升级传输的下一帧报文
* @param out_packet 升级报文
* @param finished 是否结束
* @return 是否继续
*/
bool ClientContext::build_next_upgrade_packet(std::vector<unsigned char>& out_packet, bool& finished)
{
std::lock_guard<std::mutex> lock(upgrade_mutex_);
finished = false;
out_packet.clear();
if (!upgrade_task_.active) {
return false;
}
if (upgrade_task_.next_frame_index >= upgrade_task_.total_frames) {
finished = true;
upgrade_task_.active = false;
return false;
}
std::vector<unsigned char> frame_data =
slice_frame_data(upgrade_task_.file_data,
upgrade_task_.next_frame_index,
upgrade_task_.frame_payload_size);
if (frame_data.empty()) {
upgrade_task_.active = false;
return false;
}
out_packet = generate_upgrade_start_message(
frame_data,
upgrade_task_.next_frame_index, // 0-based内部会 +1
upgrade_task_.file_size,
upgrade_task_.total_frames,
upgrade_task_.file_crc
);
++upgrade_task_.next_frame_index;
if (upgrade_task_.next_frame_index >= upgrade_task_.total_frames) {
finished = true;
}
return true;
}
/**
* @brief 获取当前升级的当前帧和总帧数
* @param current_frame_index 当前帧
* @param total_frames 总帧数
* @return 是否正常
*/
bool ClientContext::get_upgrade_progress(uint32_t& current_frame_index, uint32_t& total_frames) const
{
std::lock_guard<std::mutex> lock(upgrade_mutex_);
if (!upgrade_task_.active) {
current_frame_index = 0;
total_frames = 0;
return false;
}
current_frame_index = upgrade_task_.next_frame_index;
total_frames = upgrade_task_.total_frames;
return true;
}
void ClientContext::clear_upgrade_task()
{
std::lock_guard<std::mutex> lock(upgrade_mutex_);
upgrade_task_ = UpgradeTask{};
}
bool ClientContext::has_active_upgrade_task() const
{
std::lock_guard<std::mutex> lock(upgrade_mutex_);
return upgrade_task_.active;
}
// 版本比较函数的辅助函数:分割字符串并转换为整数向量
std::vector<int> splitVersionString(const std::string& versionStr) {
std::vector<int> segments;
@@ -1744,8 +1919,8 @@ bool ClientManager::set_preupgrade_action_to_device(const std::string& identifie
if (ctx->device_info.device_id == identifier ||
ctx->device_info.mac == identifier)
{
//设置用于校验的预升级文件
ctx->set_preupgrade_filepath(path);
//设置用于校验的预升级文件 校验采用默认生成的文件 暂时不使用
//ctx->set_preupgrade_filepath(path);
// 生成预升级报文
auto packet = generate_preupgrade_message();
@@ -1763,6 +1938,111 @@ bool ClientManager::set_preupgrade_action_to_device(const std::string& identifie
return false; // 设备未找到
}
/**
* @brief 装置升级动作 发送初始帧
* @param file 升级程序文件
* @param sin_length 上送单帧长度
* @return 发送结果
*/
bool ClientManager::send_upgrade_action_to_device(const std::string& identifier, const std::vector<unsigned char>& file,int sin_length) {
std::lock_guard<std::mutex> lock(mutex_);
for (auto& pair : clients_) {
auto& ctx = pair.second;
if (ctx->device_info.device_id == identifier ||
ctx->device_info.mac == identifier) {
//此处计算文件总大小将参数程序文件分割成依照单帧长度的多组文件计算文件crc计算保存总帧数保存当前帧计数
//之后重新写一个ClientManager的函数用于将之前计算出的参数验证正常后依次自动生成报文并执行不要直接在这里执行因为后续会在别的地方调用防止需要一直获取所有参数再生成报文直接打包方便调用。
// 1. 先准备升级任务
if (!ctx->prepare_upgrade_task(file, static_cast<uint32_t>(sin_length))) {
return false;
}
// 2. 生成首帧升级报文
std::vector<unsigned char> first_packet;
bool finished = false;
if (!ctx->build_next_upgrade_packet(first_packet, finished) || first_packet.empty()) {
ctx->clear_upgrade_task();
return false;
}
// 添加动作到队列 (状态: 读取文件目录)
ctx->add_action(DeviceState::SET_UPGRADE, first_packet);
// 如果当前空闲则立即执行
if (ctx->current_state_ == DeviceState::IDLE) {
ctx->process_next_action();
}
return true; // 成功添加
}
}
return false;
}
/**
* @brief 装置升级动作 查询并生成下一帧报文
* @param identifier 装置标识device_id 或 mac
* @param current_frame_index 获取当前帧(0-based表示下一次要发送的帧号)
* @param total_frames 获取总帧数
* @param all_sent 是否全部发送完毕
* @param packet 输出的下一帧报文;若已发送完毕则为空
* @return 获取结果
*/
bool ClientManager::try_get_next_upgrade_packet_to_device(const std::string& identifier,
uint32_t& current_frame_index,
uint32_t& total_frames,
bool& all_sent,
std::vector<unsigned char>& packet)
{
current_frame_index = 0;
total_frames = 0;
all_sent = false;
packet.clear();
std::lock_guard<std::mutex> lock(mutex_);
for (auto& pair : clients_) {
auto& ctx = pair.second;
if (ctx->device_info.device_id == identifier ||
ctx->device_info.mac == identifier)
{
// 先获取当前进度
if (!ctx->get_upgrade_progress(current_frame_index, total_frames)) {
return false; // 没有活动升级任务
}
// 如果当前帧号已经到总帧数,说明全部发送完毕
if (current_frame_index >= total_frames) {
all_sent = true;
return true;
}
// 还有帧未发送,则只生成下一帧报文,不在这里直接发送
bool finished = false;
if (!ctx->build_next_upgrade_packet(packet, finished)) {
packet.clear();
return false;
}
// build_next_upgrade_packet 之后next_frame_index 已经自增
// 这里再更新一次进度给外部
ctx->get_upgrade_progress(current_frame_index, total_frames);
if (current_frame_index >= total_frames) {
all_sent = true;
}
return true;
}
}
return false; // 未找到设备
}
/**
* @brief 生成装置控制命令报文
* @param type 命令类型 1-装置复位2-启动录波3-启动200ms数据记录4-启动3秒数据记录
@@ -1814,10 +2094,10 @@ bool ClientManager::read_eventlog_action_to_device(const std::string& identifier
ctx->device_info.mac == identifier)
{
ctx->event_lineNo = monitorPoint;
// 生成定值描述报文
// 生成补招报文
auto packet = generate_recallevent_message(Time1, Time2, eventType, monitorPoint);
// 添加动作到队列 (状态: 读取文件目录)
// 添加动作到队列 (状态: 补招事件)
ctx->add_action(DeviceState::READING_EVENTLOG, packet);
// 如果当前空闲则立即执行