#include #include #include #include #include #include #include #include #include #include #include // 用于mkdir #include #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 从 0(1月)开始 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(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(udata[16]) << static_cast(udata[17]) << static_cast(udata[18]) << static_cast(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(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(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(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 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 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(MsgResponseType::Response_Stat)) { // 一发多收,需要在这里等待所有报文收全再组装相应数据 一帧1K 直到所有数据传送完毕 //当前帧未收全,直接退出消息处理,等待后续帧 std::cout << "mac: " << mac << " count" << static_cast(udata[10]) << std::endl; // 解析帧信息 (根据实际协议调整) int current_packet = static_cast(udata[10]); // 当前帧序号 int total_packets = Stat_PacketNum; // 总帧数 std::vector 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 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 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 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, //逻辑子设备ID,0-逻辑设备本身,无填-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 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(MsgResponseType::Response_StatTime)) { std::vector 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(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(udata[12]), next_packet_type, static_cast(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(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 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, //逻辑子设备ID,0-逻辑设备本身,无填-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 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(MsgResponseType::Response_File_Download)) { // 提取当前帧序号(12-15字节,大端序) int current_frame = (static_cast(udata[12]) << 24) | (static_cast(udata[13]) << 16) | (static_cast(udata[14]) << 8) | static_cast(udata[15]); // 提取总帧数(16-19字节,大端序) int total_frames = (static_cast(udata[16]) << 24) | (static_cast(udata[17]) << 16) | (static_cast(udata[18]) << 8) | static_cast(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 < 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(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(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 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(MsgResponseType::Response_File_Download)) { // 提取当前帧序号(12-15字节,大端序) int current_frame = (static_cast(udata[12]) << 24) | (static_cast(udata[13]) << 16) | (static_cast(udata[14]) << 8) | static_cast(udata[15]); // 提取总帧数(16-19字节,大端序) int total_frames = (static_cast(udata[16]) << 24) | (static_cast(udata[17]) << 16) | (static_cast(udata[18]) << 8) | static_cast(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 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(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(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(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 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(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_list; dz_list.reserve(bufflen / structlen); // 遍历所有定值描述结构体 for (size_t i = 0, j = 1; i < bufflen; i += structlen, j++) { // 复制数据到临时缓冲区 std::vector 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(MsgResponseType::Response_NewACK)) { std::cout << "set success" << mac << std::endl; //定值设置成功,调整为空闲,处理后续工作。 ClientManager::instance().change_device_state(id, DeviceState::IDLE); } else if (udata[8] == static_cast(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(MsgResponseType::Response_Read_InterFix)) { // 获取数据长度 size_t bufflen = parser.RecvData.size(); const size_t structlen = 2; // 每个ushort占2字节 // 存储解析结果的容器 std::vector 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(MsgResponseType::Response_Read_InterFixDes)) { // 获取接收数据 std::vector& recvData = parser.RecvData; size_t bufflen = recvData.size(); const size_t structlen = sizeof(NameFixValue); // 存储解析结果的向量 std::vector 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 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(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 control_words; control_words.reserve(bufflen / structlen); // 遍历所有控制字描述结构体 for (size_t i = 0; i < bufflen; i += structlen) { // 复制数据到临时缓冲区 std::vector 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(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(MsgResponseType::Response_NewACK)) { std::cout << "set success" << mac << std::endl; //内部定值设置成功,调整为空闲,处理后续工作。 ClientManager::instance().change_device_state(id, DeviceState::IDLE); } else if (udata[8] == static_cast(MsgResponseType::Response_NewNACK)) { std::cout << "set error" << mac << std::endl; std::cout << "reason code: " << static_cast(udata[8]) << "-" << static_cast(udata[9]) << "-" << static_cast(udata[10]) << "-" << static_cast(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(currentState) << " for device " << id << std::endl; break; } // 无论何种状态,处理完成后触发后续状态处理 ClientManager::instance().post_message_processing(id); } } }