Files
front_linux/LFtid1056/dealMsg.cpp
2025-08-08 11:16:38 +08:00

1205 lines
59 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <queue>
#include <vector>
#include <atomic>
#include <fstream>
#include <sys/stat.h> // 用于mkdir
#include <iostream>
#include "cloudfront/code/interface.h" //lnk20250708
#include "cloudfront/code/rocketmq.h" //lnk20250708
#include "client2.h"
using namespace std;
SafeMessageQueue message_queue; // 全局消息队列
//时间转换函数
time_t ConvertToTimestamp(const tagTime& time) {
struct tm t = {};
t.tm_year = time.DeviceYear - 1900; // tm_year 从 1900 开始计
t.tm_mon = time.DeviceMonth - 1; // tm_mon 从 01月开始
t.tm_mday = time.DeviceDay;
t.tm_hour = time.DeviceHour;
t.tm_min = time.DeviceMinute;
t.tm_sec = time.DeviceSecond;
// 返回时间戳(本地时间)
return mktime(&t);
}
//文件分割取字段
std::string extract_filename(const std::string& path) {
// 查找最后一个'/'的位置
size_t last_slash = path.find_last_of('/');
// 如果找到'/',则返回'/'之后的部分
if (last_slash != std::string::npos) {
return path.substr(last_slash + 1);
}
// 如果没有'/',直接返回原字符串
return path;
}
//消息处理逻辑
void process_received_message(string mac, string id,const char* data, size_t length) {
// 实际的消息处理逻辑
std::cout << "Active connections: " << mac << " id:" << id << " size:" << length << std::endl;
// 示例:解析消息并处理
//数据处理逻辑
if (length > 0) {
// 将数据转为无符号类型以便处理二进制值
const unsigned char* udata = reinterpret_cast<const unsigned char*>(data);
//对数据消息的初步处理--登录报文格式解析不出来
MessageParser parser;
bool bool_msgset = parser.SetMsg(udata, length);
//云服务登录报文
if (udata[0] == 0xEB && udata[1] == 0x90 && udata[2] == 0xEB && udata[3] == 0x90) {
//通讯状态报文
if (udata[8] == 0x01) {
std::cout << "cloud login: " << mac << " state: " << static_cast<int>(udata[16]) << static_cast<int>(udata[17]) << static_cast<int>(udata[18]) << static_cast<int>(udata[19]) << std::endl;
if (udata[19] == 0x10) {
std::cout << "cloud login: " << mac << " state: success!" << std::endl;
//装置登录成功
ClientManager::instance().set_cloud_status(id, 1); //设置了云前置登录状态为已登录
//ClientManager::instance().set_real_state_count("D002", 1,1);//登录后测试实时
//ClientManager::instance().add_file_menu_action_to_device("D002","/etc");//测试文件目录读取
//ClientManager::instance().add_file_download_action_to_device("D002", "/etc/NPQS570_VX_ZJ_2(V103).icd");//测试文件下载
//ClientManager::instance().get_fixedvalue_action_to_device(id,1);//测试获取装置测点定值数据
//ClientManager::instance().get_fixedvaluedes_action_to_device(id);//测试获取装置定值描述
//ClientManager::instance().set_fixedvalue_action_to_device();//装置修改定值测试(参数需要外部提供)
//ClientManager::instance().get_interfixedvalue_action_to_device(id);//装置获取内部定值
//ClientManager::instance().get_fixedvalucontrolword_action_to_device(id,1);//获取 1-内部定值描述 或者 2-控制字描述
//ClientManager::instance().set_interfixedvalue_action_to_device();装置修改内部定值测试(参数由外部提供)
}
if (udata[19] == 0x00) {
std::cout << "cloud login: " << mac << " state: fail!" << std::endl;
//装置登录失败 关闭客户端连接 等待20秒重新登录
ClientManager::instance().restart_device(id);
}
}
else {
std::cout << "cloud login: " << mac << " state: error!"<< std::endl;
//装置登录失败 关闭客户端连接 等待20秒重新登录
ClientManager::instance().restart_device(id);
}
//登录报文处理完毕,当前报文处理逻辑结束并返回
return;
}
//装置主动上送报文 暂态事件报文/暂态波形文件报文
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_Event)) {
//处理主动上送的暂态事件报文
NewTaglogbuffer event = NewTaglogbuffer::createFromData(parser.RecvData.data(), parser.RecvData.size());
// 获取测点参数
std::string strScale;//电压等级
int nPTType;//接线方式
float fPT = 1.0f;
float fCT = 1.0f;
if (ClientManager::instance().get_point_scale_and_pttype(
id, // 或使用id
event.head.name, // 从报文中解析出的测点序号
strScale,
nPTType) && ClientManager::instance().get_pt_ct_ratio(id, event.head.name, fPT, fCT))
{
// 使用获取的参数解析事件记录
QVVRRecord record = DynamicLog_GetQVVRRecordFromLogBuffer(
strScale, nPTType, fPT, event);
// 使用记录数据(示例:打印到控制台)
std::cout << "事件类型: " << record.nType
<< ", 持续时间: " << record.fPersisstime << "s"
<< ", 特征幅值: " << record.fMagntitude << " pu"
<< ", 时间戳: " << record.triggerTimeMs << "ms" << std::endl;
//lnk20250805 事件上送先记录,录波文件上传结束后再更新文件
transfer_json_qvvr_data(id,event.head.name,
record.fMagntitude,record.fPersisstime,record.triggerTimeMs,record.nType,record.phase,
"");
}
else {
// 处理获取失败的情况
std::cerr << "Failed to get point parameters for: " << mac << std::endl;
}
//处理完毕主动上送报文后直接退出,不要干扰装置正常应答
return;
}
else if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_ActiveSOEInfo)) {
//处理主动上送的波形文件信息报文
unsigned char file_type = udata[12];//录波文件类型数 cfg dat hdr 1-3
unsigned char line_id = udata[13];//录波测点 1-6
const uint8_t* data_ptr = parser.RecvData.data() + 2;//数据体去除前两位
size_t data_size = parser.RecvData.size() - 2;
// 直接构造字符串(避免额外拷贝)
std::string tempfilename(
reinterpret_cast<const char*>(data_ptr),
data_size
);
// ========== 新增文件路径处理逻辑 ==========
// 1. 分割原始文件名和后缀
size_t dotPos = tempfilename.find_last_of('.');
std::string baseName, originalExt;
if (dotPos != std::string::npos) {
baseName = tempfilename.substr(0, dotPos);
originalExt = tempfilename.substr(dotPos);
}
else {
baseName = tempfilename;
originalExt = "";
}
// 2. 确定大小写风格
bool isUppercase = false;
if (!originalExt.empty()) {
isUppercase = true;
for (char c : originalExt) {
if (std::isalpha(c) && std::islower(c)) {
isUppercase = false;
break;
}
}
}
// 3. 生成需要的后缀列表
std::vector<std::string> requiredExts;
if (file_type == 3) { // 需要三个文件
requiredExts = { ".cfg", ".dat", ".hdr" };
}
else { // 默认需要两个文件
requiredExts = { ".cfg", ".dat" };
//requiredExts = { ".dat" };
}
// 4. 调整后缀大小写
if (isUppercase) {
for (auto& ext : requiredExts) {
for (char& c : ext) {
if (std::isalpha(c)) c = std::toupper(c);
}
}
}
// 5. 构建完整路径列表
std::vector<std::string> fullFilenames;
for (const auto& ext : requiredExts) {
fullFilenames.push_back(baseName + ext);
}
// 6. 打印结果(实际使用中可能需要替换这里的打印逻辑)
std::cout << "Generated filenames: ";
for (const auto& name : fullFilenames) {
std::cout << name << " ";
}
std::cout << std::endl;
//lnk20250805录波文件目录接口
assign_qvvr_file_list(id, line_id, fullFilenames);
// ========== 新增:为每个文件生成下载请求 ==========
for (const auto& filename : fullFilenames) {
// 生成下载请求报文 (帧序号固定为1代表开始新文件的下载)
auto downloadMsg = generate_downloadfile_message(1, filename);
// 将下载动作添加到设备队列
ClientManager::instance().add_action_to_device(
id,
DeviceState::READING_EVENTFILE,
downloadMsg
);
std::cout << "Added download request for: " << filename << std::endl;
}
//最后报文收发处理逻辑(如果当前装置空闲则尝试执行后续动作)(如果当前装置存在其他状态则直接退出,不要干扰装置后续执行)
DeviceState currentState = DeviceState::IDLE;//获取当前装置的状态
if (!ClientManager::instance().get_device_state(id, currentState)) {
std::cerr << "Failed to get device state for: " << id << std::endl;
return;
}
switch (currentState) {
case DeviceState::IDLE:
//当前装置空闲中,可以执行后续动作
ClientManager::instance().post_message_processing(id);
break;
default:
//非空闲的其他状态直接退出即可,等待后续处理完毕后再尝试获取波形文件
break;
}
//处理完毕主动上送报文后直接退出,不要干扰装置正常应答
return;
}
//常规通讯报文
{
DeviceState currentState = DeviceState::IDLE;//获取当前装置的状态
if (!ClientManager::instance().get_device_state(id, currentState)) {
std::cerr << "Failed to get device state for: " << id << std::endl;
return;
}
// 根据装置状态处理报文
switch (currentState) {
case DeviceState::IDLE:
// 空闲状态下收到报文,可能是主动上报数据
std::cout << "IDLE state: Received active report from " << mac << std::endl;
// 这里可以添加处理主动上报数据的逻辑
break;
case DeviceState::READING_STATS:
// 读取统计数据状态
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_Stat)) {
// 一发多收,需要在这里等待所有报文收全再组装相应数据 一帧1K 直到所有数据传送完毕
//当前帧未收全,直接退出消息处理,等待后续帧
std::cout << "mac: " << mac << " count" << static_cast<int>(udata[10]) << std::endl;
// 解析帧信息 (根据实际协议调整)
int current_packet = static_cast<int>(udata[10]); // 当前帧序号
int total_packets = Stat_PacketNum; // 总帧数
std::vector<unsigned char> packet_data(udata, udata + length);
bool complete = ClientManager::instance().add_stat_packet_to_device(
id, packet_data, current_packet, total_packets
);
//判断是否收全
if (complete) {
// 1. 获取并清空缓存数据包
auto packets = ClientManager::instance().get_and_clear_stat_packets(id);
// 2. 按帧序号排序
std::sort(packets.begin(), packets.end(),
[](const ClientContext::StatPacket& a, const ClientContext::StatPacket& b) {
return a.packet_index < b.packet_index;
});
// 3. 解析每帧数据并提取数据体
std::vector<unsigned char> full_data;
MessageParser parser;
for (const auto& packet : packets) {
// 解析单帧报文
if (!parser.SetMsg(packet.data.data(), packet.data.size())) {
std::cerr << "Failed to parse packet " << packet.packet_index
<< " for device " << id << std::endl;
continue;
}
// 将数据体添加到完整序列
full_data.insert(full_data.end(),
parser.RecvData.begin(),
parser.RecvData.end());
}
// 4. 组装 tagPqData 对象
tagPqData pq_data;
if (!pq_data.SetStructBuf(full_data.data(), full_data.size())) {
std::cerr << "Failed to assemble tagPqData for device " << id << std::endl;
}
else {
// 成功组装,可以在这里使用 pq_data 对象
std::cout << "Successfully assembled tagPqData for device: "
<< id << std::endl;
float fPT = 1.0f;
float fCT = 1.0f;
if (ClientManager::instance().get_pt_ct_ratio(id, pq_data.name, fPT, fCT)) {
// 使用获取的变比值进行数据转换
tagPqData_Float float_data;
float_data.SetFloatValue(pq_data, fPT, fCT);
float_data.name = pq_data.name;
float_data.Data_Type = pq_data.Data_Type;
// 将浮点数据添加到缓存
// 添加到缓存并检查是否收全
bool complete = ClientManager::instance().add_float_data_to_device(
id, pq_data.name, pq_data.Data_Type, float_data);
if (complete) {
// 如果收全,立即取出处理
std::array<tagPqData_Float, 4> all_data =
ClientManager::instance().get_and_clear_float_data(id, pq_data.name);
if (!all_data.empty()) {
//单个测点 4组数据处理逻辑
tagPqData_Float max_data = all_data[0];
tagPqData_Float min_data = all_data[1];
tagPqData_Float avg_data = all_data[2];
tagPqData_Float cp95_data = all_data[3];
std::string strScale;//电压等级
int nPTType = 0;//接线方式
ClientManager::instance().get_point_scale_and_pttype(
id, // 或使用id
pq_data.name, // 从报文中解析出的测点序号
strScale,
nPTType);
// 转换为Base64字符串
std::string max_base64Str = max_data.ConvertToBase64(nPTType);
std::string min_base64Str = min_data.ConvertToBase64(nPTType);
std::string avg_base64Str = avg_data.ConvertToBase64(nPTType);
std::string cp95_base64Str = cp95_data.ConvertToBase64(nPTType);
//std::cout << "New star base64Str0:" << max_base64Str << std::endl;
//std::cout << "New del base64Str1:" << avg_data.ConvertToBase64(1) << std::endl;
//lnk20250708使用接口发送
time_t data_time = ConvertToTimestamp(avg_data.time);
std::vector<DataArrayItem> arr;
arr.push_back({1, //数据属性 -1-无, 0-“Rt”,1-“Max”,2-“Min”,3-“Avg”,4-“Cp95”
data_time, //数据转换出来的时间数据时标相对1970年的秒无效填入“-1”
0, //数据时标,微秒钟,无效填入“-1”
0, //数据标识1-标识数据异常
max_base64Str});
arr.push_back({2, data_time, 0, 0, min_base64Str});
arr.push_back({3, data_time, 0, 0, avg_base64Str});
arr.push_back({4, data_time, 0, 0, cp95_base64Str});
std::string js = generate_json(
normalize_mac(mac),
-1, //需应答的报文订阅者收到后需以此ID应答无需应答填入“-1”
1, //设备唯一标识Ldid填入0代表Ndid,后续根据商议决定填id还是数字
1, //报文处理的优先级1 I类紧急请求/响应 2 II类紧急请求/响应 3 普通请求/响应 4 广播报文
0x1302, //设备数据主动上送的数据类型
avg_data.name, //逻辑子设备ID0-逻辑设备本身,无填-1
0x04, //数据类型固定为电能质量
2, //数据属性无“0”、实时“1”、统计“2”等
1, //数据集序号(以数据集方式上送),无填-1
arr //数据数组
);
//std::cout << js << std::endl;
//// 创建输出流并打开文件(覆盖模式)
//std::ofstream outFile("json.txt"); // 等价于 std::ofstream outFile(filePath, std::ios::out);
//if (outFile.is_open()) { // 检查文件是否成功打开
// outFile << js; // 写入字符串
// outFile.close(); // 关闭文件
// // 成功提示(实际应用中建议使用日志)
//}
//else {
// // 错误处理:文件打开失败(如路径不存在)
//}
queue_data_t data;
data.monitor_no = 1; //暂无意义
data.strTopic = TOPIC_STAT;//统计topic
data.strText = js;
data.mp_id = "test"; //暂无意义
std::lock_guard<std::mutex> lock(queue_data_list_mutex);
queue_data_list.push_back(data);
// 输出结果
//std::cout << "Base64 Encoded Data (" << max_data.CalculateFloatCount()
// << " floats): " << base64Str << std::endl;
}
}
}
else {
// 处理获取变比值失败的情况
std::cerr << "Failed to get PT/CT ratio for device: "
<< mac << " lineno: " << pq_data.name << std::endl;
}
}
//数据组装完毕,修改为空闲状态等待下一项工作
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
//未收全则直接结束处理,等待后续报文应答
return;
}
}
else {
// 装置答非所问异常
// 接收统计数据错误,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::READING_STATS_TIME:
// 读取统计时间状态
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_StatTime)) {
std::vector<PointInfo> points;//装置测点信息
if (ClientManager::instance().get_device_points(mac, points)) {
// 成功获取测点信息
// 处理接收装置的时标
tagTime t3;
t3.SetStructBuf(parser.RecvData.data(), parser.RecvData.size());
int first = 0;//第一次标记
for (const auto& point : points) {
for (ushort i = 0; i < 4; i++)//每个测点需要单独召唤最大最小平均95概率值
{
auto sendbuff = generate_statequerystat_message(t3, point.nCpuNo, i);//组装询问统计数据报文
if (first == 0) {
//首次尝试组装报文 直接将当前状态调整 并等待最后启动发送
first++;
ClientManager::instance().change_device_state(id, DeviceState::READING_STATS, sendbuff);
}
else {
//非首次进入,将动作传入队列等待
ClientManager::instance().add_action_to_device(id, DeviceState::READING_STATS, sendbuff);
}
}
}
}
else {
// 未找到装置下属测点异常
// 接收统计数据时间错误,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
}
else {
// 装置答非所问异常
// 接收统计数据时间错误,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::READING_REALSTAT:
//读取实时数据状态
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_New_3S)) {
unsigned char packet_type = udata[13];
//取监测点号
unsigned char cid = udata[12];
// 将数据添加到缓存
const uint8_t* data_ptr = parser.RecvData.data() + 4;
size_t data_size = parser.RecvData.size() - 4;
ClientManager::instance().add_realtime_packet_to_device(
id, packet_type, data_ptr, data_size
);
// 如果不是最后一个包,请求下一个包
if (packet_type != 0x06) {
unsigned char next_packet_type = packet_type + 1;
auto sendbuff = generate_realstat_message(
static_cast<unsigned char>(udata[12]),
next_packet_type,
static_cast<unsigned char>(0x01)
);
ClientManager::instance().change_device_state(
id, DeviceState::READING_REALSTAT, sendbuff
);
}
else {
// 获取并清空缓存
auto packets = ClientManager::instance().get_and_clear_realtime_packets(id);
// 按包类型排序01-06
std::sort(packets.begin(), packets.end(),
[](const ClientContext::RealtimePacket& a,
const ClientContext::RealtimePacket& b) {
return a.packet_type < b.packet_type;
});
RealtagPqDate_float realdata;
// 按顺序解析每个包
for (const auto& packet : packets) {
switch (packet.packet_type) {
case 0x01:
realdata.ParsePacket1(packet.data.data(), packet.data.size());
break;
case 0x02:
realdata.ParsePacket2(packet.data.data(), packet.data.size());
break;
case 0x03:
realdata.ParsePacket3(packet.data.data(), packet.data.size());
break;
case 0x04:
realdata.ParsePacket4(packet.data.data(), packet.data.size());
break;
case 0x05:
realdata.ParsePacket5(packet.data.data(), packet.data.size());
break;
case 0x06:
realdata.ParsePacket6(packet.data.data(), packet.data.size());
break;
}
}
std::string strScale;//电压等级
int nPTType = 0;//接线方式
ClientManager::instance().get_point_scale_and_pttype(
id, // 或使用id
static_cast<unsigned char>(udata[12]), // 从报文中解析出的测点序号
strScale,
nPTType);
std::string base64 = realdata.ConvertToBase64(nPTType);
//std::cout << base64 << std::endl;
//lnk实时数据使用接口发送20250711
time_t data_time = ConvertToTimestamp(realdata.time);
std::vector<DataArrayItem> arr;
arr.push_back({1, //数据属性 -1-无, 0-“Rt”,1-“Max”,2-“Min”,3-“Avg”,4-“Cp95”
data_time, //数据转换出来的时间数据时标相对1970年的秒无效填入“-1”
0, //数据时标,微秒钟,无效填入“-1”
0, //数据标识1-标识数据异常
base64});
std::string js = generate_json(
normalize_mac(mac),
-1, //需应答的报文订阅者收到后需以此ID应答无需应答填入“-1”
1, //设备唯一标识Ldid填入0代表Ndid,后续根据商议决定填id还是数字
1, //报文处理的优先级1 I类紧急请求/响应 2 II类紧急请求/响应 3 普通请求/响应 4 广播报文
0x1302, //设备数据主动上送的数据类型
cid, //逻辑子设备ID0-逻辑设备本身,无填-1
0x04, //数据类型固定为电能质量数据
1, //数据属性无“0”、实时“1”、统计“2”等
1, //数据集序号(以数据集方式上送),无填-1
arr //数据数组
);
//std::cout << js << std::en
queue_data_t data;
data.monitor_no = 1; //暂无意义
data.strTopic = TOPIC_RTDATA; //实时topic
data.strText = js;
data.mp_id = "test"; //暂无意义
std::lock_guard<std::mutex> lock(queue_data_list_mutex);
queue_data_list.push_back(data);
// 处理完成后重置状态
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
}
else {
// 装置答非所问异常
// 接收实时数据错误,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::READING_EVENTFILE:
// 暂态波形文件下载
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_File_Download)) {
// 提取当前帧序号12-15字节大端序
int current_frame = (static_cast<int>(udata[12]) << 24) |
(static_cast<int>(udata[13]) << 16) |
(static_cast<int>(udata[14]) << 8) |
static_cast<int>(udata[15]);
// 提取总帧数16-19字节大端序
int total_frames = (static_cast<int>(udata[16]) << 24) |
(static_cast<int>(udata[17]) << 16) |
(static_cast<int>(udata[18]) << 8) |
static_cast<int>(udata[19]);
//std::cout << "eventfile frames: " << current_frame << "/" << total_frames << std::endl;
// 将数据添加到缓存 去除数据体前14位 (逻辑稍后编写)
const uint8_t* data_ptr = parser.RecvData.data() + 14;
size_t data_size = parser.RecvData.size() - 14;
// 如果是第一帧,记录文件名
if (current_frame == 1) {
ClientManager::DownloadInfo info;
if (ClientManager::instance().parse_download_packet(id, info)) {
ClientManager::instance().update_current_filename(id, info.filename);
}
}
// 获取文件名
std::string filename = ClientManager::instance().get_current_filename(id);
// 添加到缓存
ClientManager::instance().add_file_packet_to_device(id, current_frame, data_ptr, data_size);
//std::cout << "fileinfo: " << info.filename << "/" << info.current_frame << std::endl;
//判断是否收全,未收全则继续发送报文,收全则取出所有缓存组装文件并保存至本地,推送消息
if (current_frame < total_frames) {
// 未收全,更新帧序号并保持状态,等待后续自动发送已修改的新报文
int nextframe = current_frame + 1;
auto downloadMsg = generate_downloadfile_message(nextframe, filename);
ClientManager::instance().change_device_state(id, DeviceState::READING_EVENTFILE, downloadMsg);
}
else {
// 已收全,在此处处理文件
std::cout << "mac: " << mac << " fileinfo: " << filename <<std::endl;
// 获取缓存中的所有分片
auto packets = ClientManager::instance().get_and_clear_file_packets(id);
// 合并文件内容
std::vector<unsigned char> file_data;
for (const auto& packet : packets) {
file_data.insert(file_data.end(), packet.begin(), packet.end());
}
// 保存文件
std::string wavefile = "wave"; // 使用MAC地址作为目录名
// 创建目录(如果不存在)
if (mkdir(wavefile.c_str(), 0777) != 0 && errno != EEXIST) {
std::cerr << "Failed to create directory: " << wavefile << std::endl;
}
// 保存文件
std::string mac_dir = wavefile + "/" + mac; // 使用MAC地址作为目录名
// 创建目录(如果不存在)
if (mkdir(mac_dir.c_str(), 0777) != 0 && errno != EEXIST) {
std::cerr << "Failed to create directory: " << mac_dir << std::endl;
}
std::string path = extract_filename(filename);
std::string file_path = mac_dir + "/" + path;
std::ofstream out_file(file_path, std::ios::binary);
if (out_file) {
out_file.write(reinterpret_cast<const char*>(file_data.data()),
file_data.size());
std::cout << "File saved: " << file_path << std::endl;
//lnk20250805文件保存成功调用录波文件上送接口
update_qvvr_file_download(file_path, id);
}
else {
std::cerr << "Failed to save file: " << file_path
<< ", Error: " << strerror(errno) << std::endl;
}
//当前文件下载完毕,调整为空闲处理下一项工作(如果这里后续有新文件等待下载,一般已经存入等待队列等候处理了,调成空闲状态后直接就会开始新文件的下载工作)
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
}
else {
// 装置答非所问异常
// 接收波形文件数据错误,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::READING_FILEMENU:
//读取文件目录
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_FileDir)) {
// 计算结构体大小
const size_t struct_size = sizeof(tag_dir_info);
const uint8_t* data_ptr = parser.RecvData.data();
size_t data_size = parser.RecvData.size();
std::vector<tag_dir_info> FileList;
// 遍历接收到的数据
for (size_t i = 0; i < data_size; i += struct_size) {
if (i + struct_size > data_size) break;
tag_dir_info dir_info;
memcpy(&dir_info, data_ptr + i, struct_size);
// 字节序转换 (大端 -> 小端)
dir_info.flag = ntohl(dir_info.flag);
dir_info.size = ntohl(dir_info.size);
std::string gbk_name(dir_info.name, strnlen(dir_info.name, sizeof(dir_info.name)));
// 打印文件名
std::cout << "file name:" << gbk_name << std::endl;
// 添加到文件列表
FileList.push_back(dir_info);
}
// 这里可以添加发送文件列表的逻辑
// 例如: send_file_list(FileList);
// 处理完成后重置状态
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问异常
// 接收目录数据错误,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::READING_FILEDATA:
//下载文件数据 和暂态文件下载共用同一功能码
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_File_Download))
{
// 提取当前帧序号12-15字节大端序
int current_frame = (static_cast<int>(udata[12]) << 24) |
(static_cast<int>(udata[13]) << 16) |
(static_cast<int>(udata[14]) << 8) |
static_cast<int>(udata[15]);
// 提取总帧数16-19字节大端序
int total_frames = (static_cast<int>(udata[16]) << 24) |
(static_cast<int>(udata[17]) << 16) |
(static_cast<int>(udata[18]) << 8) |
static_cast<int>(udata[19]);
//提取数据
const uint8_t* data_ptr = parser.RecvData.data() + 14;
size_t data_size = parser.RecvData.size() - 14;
// 如果是第一帧,记录文件名
if (current_frame == 1) {
ClientManager::DownloadInfo info;
if (ClientManager::instance().parse_download_packet(id, info)) {
ClientManager::instance().update_current_filename(id, info.filename);
}
}
// 获取文件名
std::string filename = ClientManager::instance().get_current_filename(id);
// 添加到缓存
ClientManager::instance().add_file_packet_to_device(id, current_frame, data_ptr, data_size);
//判断是否收全,未收全则继续发送报文,收全则取出所有缓存组装文件并保存至本地,推送消息
if (current_frame < total_frames) {
// 未收全,更新帧序号并保持状态,等待后续自动发送已修改的新报文
int nextframe = current_frame + 1;
auto downloadMsg = generate_downloadfile_message(nextframe, filename);
ClientManager::instance().change_device_state(id, DeviceState::READING_FILEDATA, downloadMsg);
}
else {
// 已收全,在此处处理文件
std::cout << "mac: " << mac << " fileinfo: " << filename << std::endl;
// 获取缓存中的所有分片
auto packets = ClientManager::instance().get_and_clear_file_packets(id);
// 合并文件内容
std::vector<unsigned char> file_data;
for (const auto& packet : packets) {
file_data.insert(file_data.end(), packet.begin(), packet.end());
}
// 保存文件
std::string wavefile = "download"; // 使用MAC地址作为目录名
// 创建目录(如果不存在)
if (mkdir(wavefile.c_str(), 0777) != 0 && errno != EEXIST) {
std::cerr << "Failed to create directory: " << wavefile << std::endl;
}
// 保存文件
std::string mac_dir = wavefile + "/" + mac; // 使用MAC地址作为目录名
// 创建目录(如果不存在)
if (mkdir(mac_dir.c_str(), 0777) != 0 && errno != EEXIST) {
std::cerr << "Failed to create directory: " << mac_dir << std::endl;
}
std::string path = extract_filename(filename);
std::string file_path = mac_dir + "/" + path;
std::ofstream out_file(file_path, std::ios::binary);
if (out_file) {
out_file.write(reinterpret_cast<const char*>(file_data.data()),
file_data.size());
std::cout << "File saved: " << file_path << std::endl;
}
else {
std::cerr << "Failed to save file: " << file_path
<< ", Error: " << strerror(errno) << std::endl;
}
//当前文件下载完毕,调整为空闲处理下一项工作(如果这里后续有新文件等待下载,一般已经存入等待队列等候处理了,调成空闲状态后直接就会开始新文件的下载工作)
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
}
else {
// 装置答非所问异常
// 下载文件数据错误,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::READING_FIXEDVALUE:
//读取指定测点定值数据
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_FixValue)) {
// 确保数据长度足够包含测点序号
if (parser.RecvData.size() < 1) {
std::cout << "Invalid fix value data length" << std::endl;
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
break;
}
// 提取测点序号 (第一个字节)
uint8_t monitor_index = parser.RecvData[0];
std::cout << "Monitor Index: " << static_cast<int>(monitor_index) << std::endl;
// 计算有效数据长度 (排除测点序号)
size_t bufflen = parser.RecvData.size() - 1;
const size_t structlen = 4; // 每个浮点数占4字节
// 检查数据长度是否合法
if (bufflen % structlen != 0) {
std::cout << "Invalid fix value data length: " << bufflen
<< " (not multiple of 4)" << std::endl;
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
break;
}
// 存储浮点值的容器
std::vector<float> fList;
fList.reserve(bufflen / structlen); // 预分配空间
// 解析浮点数据 (从第二个字节开始)
for (size_t i = 1; i < parser.RecvData.size(); i += structlen) {
// 复制4字节数据
uint8_t bytes[4] = {
parser.RecvData[i],
parser.RecvData[i + 1],
parser.RecvData[i + 2],
parser.RecvData[i + 3]
};
// 翻转字节序 (大端转小端)
std::swap(bytes[0], bytes[3]);
std::swap(bytes[1], bytes[2]);
// 转换为float
float value;
memcpy(&value, bytes, sizeof(float));
fList.push_back(value);
}
// 打印解析结果
std::cout << "Parsed " << fList.size() << " fix values:" << std::endl;
for (size_t j = 0; j < fList.size(); ++j) {
std::cout << " Value[" << j << "]: " << fList[j] << std::endl;
}
//测试定值修改功能
//ClientManager::instance().set_fixedvalue_action_to_device(id, monitor_index, fList);
//定值读取完毕,调整为空闲,处理后续工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问异常
// 读取定值错误,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::READING_FIXEDVALUEDES:
//读取指定测点定值描述
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_FixDes)) {
// 计算结构体大小
const size_t structlen = sizeof(DZ_TAB_STRUCT);
const size_t bufflen = parser.RecvData.size();
// 检查数据长度是否有效
if (bufflen == 0 || bufflen % structlen != 0) {
std::cerr << "Invalid fixdes data length: " << bufflen
<< " (expected multiple of " << structlen << ")" << std::endl;
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
break;
}
// 存储所有解析出的定值描述
std::vector<DZ_TAB_STRUCT> dz_list;
dz_list.reserve(bufflen / structlen);
// 遍历所有定值描述结构体
for (size_t i = 0, j = 1; i < bufflen; i += structlen, j++) {
// 复制数据到临时缓冲区
std::vector<uint8_t> buff(structlen);
memcpy(buff.data(), parser.RecvData.data() + i, structlen);
// 执行字节序转换与C#相同)
ReversalBuff(buff.data(), 0, 2); // LN_Num
ReversalBuff(buff.data(), 2, 2); // DZ_Num
ReversalBuff(buff.data(), 70, 2); // DZ_Type
ReversalBuff(buff.data(), 72, 4); // DZ_Min
ReversalBuff(buff.data(), 76, 4); // DZ_Max
ReversalBuff(buff.data(), 80, 4); // DZ_Default
// 解析为结构体
DZ_TAB_STRUCT dz_info;
memcpy(&dz_info, buff.data(), structlen);
// 正确处理字符串 - 查找第一个'\0'作为结束符
auto find_string_end = [](const char* arr, size_t max_len) -> size_t {
for (size_t i = 0; i < max_len; i++) {
if (arr[i] == '\0') return i;
}
return max_len;
};
// 提取原始GBK字符串
size_t name_len = find_string_end(dz_info.DZ_Name, sizeof(dz_info.DZ_Name));
size_t unit_len = find_string_end(dz_info.DZ_UNIT, sizeof(dz_info.DZ_UNIT));
// 转换为可打印字符串(可选)(GBK编码)
std::string dz_name(dz_info.DZ_Name, name_len);
std::string dz_unit(dz_info.DZ_UNIT, unit_len);
// 保存到上下文
dz_list.push_back(dz_info);
// 调试输出(可选)
std::cout << "Parsed DZ entry #" << j << ": "
<< "LN=" << dz_info.LN_Num
<< ", ID=" << dz_info.DZ_Num
<< ", Name=" << dz_name
<< ", Type=" << dz_info.DZ_Type
<< ", Min=" << dz_info.DZ_Min
<< ", Max=" << dz_info.DZ_Max
<< ", Default=" << dz_info.DZ_Default
<< ", Unit=" << dz_unit << std::endl;
}
//定值描述读取完毕,调整为空闲,处理后续工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问异常
// 读取定值描述,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::SET_FIXEDVALUE:
//设置装置定值
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewACK)) {
std::cout << "set success" << mac << std::endl;
//定值设置成功,调整为空闲,处理后续工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewNACK)) {
std::cout << "set error" << mac << std::endl;
// 装置否定应答,定值设置失败
// 设置装置定值失败,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问异常
// 设置装置定值失败,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::READING_INTERFIXEDVALUE:
//读取内部定值
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_Read_InterFix)) {
// 获取数据长度
size_t bufflen = parser.RecvData.size();
const size_t structlen = 2; // 每个ushort占2字节
// 存储解析结果的容器
std::vector<ushort> fList;
fList.reserve(bufflen / structlen); // 预分配空间
// 解析每个ushort数据
for (size_t i = 0; i < bufflen; i += structlen) {
// 复制2字节数据
uint8_t bytes[2] = {
parser.RecvData[i],
parser.RecvData[i + 1]
};
// 翻转字节序 (大端转小端)
std::swap(bytes[0], bytes[1]);
// 转换为ushort - 使用memcpy确保正确处理字节序
ushort value;
memcpy(&value, bytes, sizeof(ushort));
fList.push_back(value);
}
// 打印解析结果(调试用)
std::cout << "Parsed " << fList.size() << " internal fixed values:" << std::endl;
for (size_t j = 0; j < fList.size(); ++j) {
std::cout << " Value[" << j << "]: " << fList[j] << std::endl;
}
//内部定值修改测试
//ClientManager::instance().set_interfixedvalue_action_to_device(id, fList);
//内部定值获取完毕,调整为空闲,处理后续工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问异常
// 读取装置内部定值失败,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::READING_INTERFIXEDVALUEDES:
//读取内部定值描述
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_Read_InterFixDes)) {
// 获取接收数据
std::vector<uint8_t>& recvData = parser.RecvData;
size_t bufflen = recvData.size();
const size_t structlen = sizeof(NameFixValue);
// 存储解析结果的向量
std::vector<NameFixValue> fixValueList;
// 检查数据长度是否合法
if (bufflen == 0 || bufflen % structlen != 0) {
std::cerr << "Invalid internal fixdes data length: " << bufflen
<< " (expected multiple of " << structlen << ")" << std::endl;
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
break;
}
else
{
// 计算结构体数量
size_t structCount = bufflen / structlen;
fixValueList.reserve(structCount);
// 遍历所有结构体
for (size_t i = 0, k = 1; i < bufflen; i += structlen, k++)
{
// 复制当前结构体数据到缓冲区
std::vector<uint8_t> buff(structlen);
memcpy(buff.data(), recvData.data() + i, structlen);
// 翻转数据类型DataType (偏移22, 2字节)
ReversalBuff(buff.data(), 22, 2);
// 翻转最小值MinValue (偏移24, 2字节)
ReversalBuff(buff.data(), 24, 2);
// 翻转最大值MaxValue (偏移26, 2字节)
ReversalBuff(buff.data(), 26, 2);
// 翻转缺省值DefaultValue (偏移28, 2字节)
ReversalBuff(buff.data(), 28, 2);
// 解析为结构体
NameFixValue dz_info;
memcpy(&dz_info, buff.data(), structlen);
// 添加到结果列表
fixValueList.push_back(dz_info);
// 调试输出
std::string fixName(dz_info.sFixValueName,
strnlen(dz_info.sFixValueName, sizeof(dz_info.sFixValueName)));
std::string dimension(dz_info.sDimension,
strnlen(dz_info.sDimension, sizeof(dz_info.sDimension)));
std::cout << "Parsed internal fix value #" << k << ": "
<< "Name=" << fixName << ", "
<< "Type=" << dz_info.DataType << ", "
<< "Min=" << dz_info.MinValue << ", "
<< "Max=" << dz_info.MaxValue << ", "
<< "Default=" << dz_info.DefaultValue << ", "
<< "Unit=" << dimension << std::endl;
}
}
//内部定值描述获取完毕,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问异常
// 读取装置内部定值描述失败,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::READING_CONTROLWORD:
//读取控制字描述
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_Read_InterFixDes)) {
// 计算结构体大小
const size_t structlen = sizeof(DZ_kzz_bit);
const size_t bufflen = parser.RecvData.size();
// 检查数据长度是否有效
if (bufflen == 0 || bufflen % structlen != 0) {
std::cerr << "Invalid control word data length: " << bufflen
<< " (expected multiple of " << structlen << ")" << std::endl;
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
break;
}
// 存储解析结果
std::vector<DZ_kzz_bit> control_words;
control_words.reserve(bufflen / structlen);
// 遍历所有控制字描述结构体
for (size_t i = 0; i < bufflen; i += structlen) {
// 复制数据到临时缓冲区
std::vector<uint8_t> buff(structlen);
memcpy(buff.data(), parser.RecvData.data() + i, structlen);
// 解析为结构体
DZ_kzz_bit dz_info;
memcpy(&dz_info, buff.data(), structlen);
// 正确处理字符串 - 查找第一个'\0'作为结束符
auto find_string_end = [](const char* arr, size_t max_len) -> size_t {
for (size_t i = 0; i < max_len; i++) {
if (arr[i] == '\0') return i;
}
return max_len;
};
// 提取原始GBK字符串
size_t name_len = find_string_end(dz_info.kzz_bit, sizeof(dz_info.kzz_bit));
// 直接存储原始GBK数据不转换UTF-8
control_words.push_back(dz_info);
// 调试输出(可选)
std::string gbk_name(dz_info.kzz_bit,name_len);
std::cout << "Control word: " << gbk_name
<< ", enable: " << static_cast<int>(dz_info.bit_enable) << std::endl;
}
// 控制字描述读取完毕,调整为空闲,处理后续工作
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问异常
// 读取装置控制字描述失败,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::SET_INTERFIXEDVALUE:
//设置内部定值
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewACK)) {
std::cout << "set success" << mac << std::endl;
//内部定值设置成功,调整为空闲,处理后续工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewNACK)) {
std::cout << "set error" << mac << std::endl;
std::cout << "reason code: " << static_cast<int>(udata[8]) << "-" << static_cast<int>(udata[9]) << "-" << static_cast<int>(udata[10]) << "-" << static_cast<int>(udata[11]) << std::endl;
// 装置否定应答,内部定值设置失败
// 设置装置内部定值失败,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问异常
// 设置装置内部定值失败,调整为空闲状态,处理下一项工作。
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
case DeviceState::CUSTOM_ACTION:
// 自定义动作状态
std::cout << "CUSTOM_ACTION state: Processing custom response from " << mac << std::endl;
// 这里添加处理自定义动作响应的逻辑
// 处理完成后标记状态完成
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
break;
default:
std::cerr << "Unknown state: " << static_cast<int>(currentState)
<< " for device " << id << std::endl;
break;
}
// 无论何种状态,处理完成后触发后续状态处理
ClientManager::instance().post_message_processing(id);
}
}
}