新增文件删除 文件上传 目录创建 目录删除

This commit is contained in:
2026-03-31 09:44:17 +08:00
parent 69accad937
commit dfe0f2e5e2
5 changed files with 750 additions and 4 deletions

View File

@@ -64,6 +64,30 @@ static uint32_t CalcNormalCrc32_Char(const std::vector<unsigned char>& bytes,
return crc;
}
// 与 C# Crc_16_new 完全对应Modbus CRC16
static uint16_t CalcCrc16_Char(const std::vector<unsigned char>& bytes)
{
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < bytes.size(); ++i)
{
crc = static_cast<uint16_t>(crc ^ static_cast<uint16_t>(bytes[i]));
for (int j = 0; j < 8; ++j)
{
if ((crc & 0x0001) == 0x0001)
{
crc = static_cast<uint16_t>((crc >> 1) ^ 0xA001);
}
else
{
crc = static_cast<uint16_t>(crc >> 1);
}
}
}
return crc;
}
// ClientContext 实现
ClientContext::ClientContext(uv_loop_t* loop, const DeviceInfo& device, int index)
: loop(loop), state(ConnectionState::DISCONNECTED),
@@ -523,6 +547,120 @@ bool ClientContext::has_active_upgrade_task() const
return upgrade_task_.active;
}
/**
* @brief 文件上送参数初始化
* @param file 上送文件内容
* @param frame_payload_size 单帧数据长
* @param dest_file_path 装置端目标全路径
* @return 是否初始化成功
*/
bool ClientContext::prepare_send_file_task(const std::vector<unsigned char>& file,
uint32_t frame_payload_size,
const std::string& dest_file_path)
{
if (file.empty() || frame_payload_size == 0 || dest_file_path.empty()) {
return false;
}
std::lock_guard<std::mutex> lock(send_file_mutex_);
send_file_task_.file_data = file;
send_file_task_.file_size = static_cast<uint32_t>(file.size());
send_file_task_.file_crc = CalcCrc16_Char(file);
send_file_task_.frame_payload_size = frame_payload_size;
send_file_task_.total_frames =
static_cast<uint32_t>((file.size() + frame_payload_size - 1) / frame_payload_size);
send_file_task_.next_frame_index = 0;
send_file_task_.dest_file_path = dest_file_path;
send_file_task_.active = (send_file_task_.total_frames > 0);
return send_file_task_.active;
}
/**
* @brief 生成文件上送的下一帧报文
* @param out_packet 上送报文
* @param finished 是否结束
* @return 是否继续
*/
bool ClientContext::build_next_send_file_packet(std::vector<unsigned char>& out_packet, bool& finished)
{
std::lock_guard<std::mutex> lock(send_file_mutex_);
finished = false;
out_packet.clear();
if (!send_file_task_.active) {
return false;
}
if (send_file_task_.next_frame_index >= send_file_task_.total_frames) {
finished = true;
send_file_task_.active = false;
return false;
}
std::vector<unsigned char> frame_data =
slice_frame_data(send_file_task_.file_data,
send_file_task_.next_frame_index,
send_file_task_.frame_payload_size);
if (frame_data.empty()) {
send_file_task_.active = false;
return false;
}
out_packet = generate_sendfile_message(
frame_data,
send_file_task_.next_frame_index, // 0-based函数内部会 +1
send_file_task_.total_frames,
send_file_task_.file_size,
send_file_task_.file_crc,
send_file_task_.dest_file_path
);
++send_file_task_.next_frame_index;
if (send_file_task_.next_frame_index >= send_file_task_.total_frames) {
finished = true;
}
return true;
}
/**
* @brief 获取当前文件上送的当前帧和总帧数
* @param current_frame_index 当前帧
* @param total_frames 总帧数
* @return 是否正常
*/
bool ClientContext::get_send_file_progress(uint32_t& current_frame_index, uint32_t& total_frames) const
{
std::lock_guard<std::mutex> lock(send_file_mutex_);
if (!send_file_task_.active) {
current_frame_index = 0;
total_frames = 0;
return false;
}
current_frame_index = send_file_task_.next_frame_index;
total_frames = send_file_task_.total_frames;
return true;
}
void ClientContext::clear_send_file_task()
{
std::lock_guard<std::mutex> lock(send_file_mutex_);
send_file_task_ = SendFileTask{};
}
bool ClientContext::has_active_send_file_task() const
{
std::lock_guard<std::mutex> lock(send_file_mutex_);
return send_file_task_.active;
}
// 版本比较函数的辅助函数:分割字符串并转换为整数向量
std::vector<int> splitVersionString(const std::string& versionStr) {
std::vector<int> segments;
@@ -1653,6 +1791,108 @@ bool ClientManager::add_file_download_action_to_device(
return false; // 设备未找到
}
/**
* @brief 生成删除装置文件报文
* @param file_path 文件全路径
* @return 发送结果
*/
bool ClientManager::add_file_delete_action_to_device(
const std::string& identifier,
const std::string& file_path) {
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)
{
// 生成文件删除请求报文
auto delMsg = generate_deletefile_message(file_path);
// 添加动作到队列 (状态: 删除指定文件)
ctx->add_action(DeviceState::DEL_FILE, delMsg);
// 如果当前空闲则立即执行
if (ctx->current_state_ == DeviceState::IDLE) {
ctx->process_next_action();
}
return true; // 成功添加
}
}
return false; // 设备未找到
}
/**
* @brief 生成创建目录报文
* @param file_path 文件全路径
* @return 发送结果
*/
bool ClientManager::add_menu_set_action_to_device(
const std::string& identifier,
const std::string& file_path) {
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)
{
// 生成目录创建请求报文
auto delMsg = generate_setmenu_message(file_path);
// 添加动作到队列 (状态: 创建目录)
ctx->add_action(DeviceState::SEND_MENU, delMsg);
// 如果当前空闲则立即执行
if (ctx->current_state_ == DeviceState::IDLE) {
ctx->process_next_action();
}
return true; // 成功添加
}
}
return false; // 设备未找到
}
/**
* @brief 生成删除目录报文
* @param file_path 文件全路径
* @return 发送结果
*/
bool ClientManager::add_menu_del_action_to_device(
const std::string& identifier,
const std::string& file_path) {
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)
{
// 生成目录删除请求报文
auto delMsg = generate_delmenu_message(file_path);
// 添加动作到队列 (状态: 删除目录)
ctx->add_action(DeviceState::DEL_MENU, delMsg);
// 如果当前空闲则立即执行
if (ctx->current_state_ == DeviceState::IDLE) {
ctx->process_next_action();
}
return true; // 成功添加
}
}
return false; // 设备未找到
}
//获取指定装置指定测点下的定值数据
bool ClientManager::get_fixedvalue_action_to_device(const std::string& identifier, ushort point_id) {
std::lock_guard<std::mutex> lock(mutex_);
@@ -1908,7 +2148,7 @@ bool ClientManager::read_devversion_action_to_device(const std::string& identifi
/**
* @brief 生成预升级校验报文
* @param path 预校验文件路径
* @return 包含完整报文的字节向量
* @return 发送结果
*/
bool ClientManager::set_preupgrade_action_to_device(const std::string& identifier, const std::string& path) {
std::lock_guard<std::mutex> lock(mutex_);
@@ -2043,6 +2283,115 @@ bool ClientManager::try_get_next_upgrade_packet_to_device(const std::string& ide
return false; // 未找到设备
}
/**
* @brief 文件上送动作 发送初始帧
* @param file 上送文件内容
* @param sin_length 上送单帧长度
* @param dest_file_path 装置端目标全路径
* @return 发送结果
*/
bool ClientManager::send_file_action_to_device(const std::string& identifier,
const std::vector<unsigned char>& file,
int sin_length,
const std::string& dest_file_path)
{
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) {
// 1. 先准备文件上送任务
if (!ctx->prepare_send_file_task(file,
static_cast<uint32_t>(sin_length),
dest_file_path)) {
return false;
}
// 2. 生成首帧上送报文
std::vector<unsigned char> first_packet;
bool finished = false;
if (!ctx->build_next_send_file_packet(first_packet, finished) ||
first_packet.empty()) {
ctx->clear_send_file_task();
return false;
}
// 3. 添加动作到队列
ctx->add_action(DeviceState::SEND_FILE, first_packet);
// 4. 如果当前空闲则立即执行
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_send_file_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_send_file_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_send_file_packet(packet, finished)) {
packet.clear();
return false;
}
// build_next_send_file_packet 之后next_frame_index 已自增
ctx->get_send_file_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秒数据记录