add interface

This commit is contained in:
lnk
2026-04-02 16:23:15 +08:00
parent 2dab1369f3
commit ea176eceaf
6 changed files with 457 additions and 13 deletions

View File

@@ -133,6 +133,81 @@ void SendJsonAPI_web(const std::string& strUrl, //接口路径
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////下载文件流式接口
size_t req_reply_file_web(void* ptr, size_t size, size_t nmemb, void* userdata)
{
std::ofstream* out = static_cast<std::ofstream*>(userdata);
size_t total = size * nmemb;
if (out && out->is_open()) {
out->write(static_cast<const char*>(ptr), total);
}
return total;
}
bool DownloadFileAPI_web(const std::string& strUrl, // 接口路径
const std::string& code, // URL参数
const std::string& json, // POST body可为空
const std::string& save_path)// 本地保存路径
{
CURL* curl = curl_easy_init();
CURLcode res;
if (!curl) {
std::cerr << ">>> web curl init failed" << std::endl;
return false;
}
std::ofstream outFile(save_path.c_str(), std::ios::out | std::ios::binary);
if (!outFile.is_open()) {
std::cerr << ">>> open file failed: " << save_path << std::endl;
curl_easy_cleanup(curl);
return false;
}
std::string fullUrl = strUrl + "?" + code;
std::cout << ">>>file " << fullUrl << std::endl;
curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply_file_web);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &outFile);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60);
if (!json.empty()) {
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
}
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
outFile.close();
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
std::cerr << "web failed, res code: " << curl_easy_strerror(res) << std::endl;
remove(save_path.c_str()); // 下载失败删除残文件
return false;
}
if (http_code != 200) {
std::cerr << "web http failed, http_code: " << http_code << std::endl;
remove(save_path.c_str());
return false;
}
std::cout << ">>> file download success: " << save_path << std::endl;
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////上传文件接口
//处理文件上传响应
@@ -311,6 +386,105 @@ void Fileupload_test()
std::cout << "wavepath:" << wavepath << std::endl;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////下载文件接口通用
// 下载文件:从远端路径下载到本地,并返回本地文件路径
// 入参dev设备、remote_path远端完整路径
// 返回:本地保存路径(失败返回空字符串)
std::string getfilefromweb(const std::string& devid, const std::string& remote_path)
{
try {
terminal_dev* dev = nullptr;
{
std::lock_guard<std::mutex> lock(ledgermtx);
for (auto& d : terminal_devlist) {
if (d.terminal_id == devid) {
dev = &d;
break;
}
}
}
if (!dev) {
std::cerr << "[getfile][ERROR] dev not found, id=" << devid << std::endl;
return "";
}
//【1】清洗远端路径防止 \0 问题)
std::string clean_remote = sanitize(remote_path);
//【2】提取文件名去掉路径只保留最后一段
auto get_filename = [](const std::string& path) -> std::string {
size_t pos1 = path.find_last_of('/');
size_t pos2 = path.find_last_of('\\');
size_t pos = std::string::npos;
if (pos1 == std::string::npos) pos = pos2;
else if (pos2 == std::string::npos) pos = pos1;
else pos = std::max(pos1, pos2);
return (pos == std::string::npos) ? path : path.substr(pos + 1);
};
std::string filename = get_filename(clean_remote);
//【3】构造本地保存路径
std::string mac = sanitize(normalize_mac(dev->addr_str));
std::string save_dir = std::string(FRONT_PATH) + "/bin/upload/" + mac + "/";
if (!create_directory_recursive(save_dir)) {
std::cerr << "[getfile][ERROR] create dir failed: " << save_dir << std::endl;
return "";
}
std::string save_path = save_dir + filename;
std::cout << "[getfile] remote: " << clean_remote
<< " -> local: " << save_path << std::endl;
//【4】构造接口入参
//std::string fileContent;
std::string fullPath = "filePath=" + clean_remote;
std::cout << "[getfile] request param: " << fullPath << std::endl;
//【5】调用下载接口
if (!DownloadFileAPI_web(WEB_FILEDOWNLOAD, fullPath, "", save_path)) {
std::cerr << "[getfile][ERROR] download failed: " << clean_remote << std::endl;
return "";
}
/*SendJsonAPI_web(WEB_FILEDOWNLOAD, fullPath.c_str(), "", fileContent);
//【6】判断返回
if (fileContent.empty()) {
std::cerr << "[getfile][ERROR] download failed, empty content" << std::endl;
return "";
}
//【7】写文件
std::ofstream outFile(save_path, std::ios::out | std::ios::binary);
if (!outFile.is_open()) {
std::cerr << "[getfile][ERROR] cannot open file: " << save_path << std::endl;
return "";
}
outFile.write(fileContent.c_str(), fileContent.size());
outFile.close();*/
std::cout << "[getfile] File saved successfully: " << save_path << std::endl;
//【8】返回本地路径
return save_path;
}
catch (const std::exception& e) {
std::cerr << "[getfile][EXCEPTION] " << e.what() << std::endl;
}
catch (...) {
std::cerr << "[getfile][EXCEPTION] unknown error" << std::endl;
}
return "";
}
//////////////////////////////////////////////////////////////////////////////////////////////////////映射文件下载接口
void download_xml_for_icd(const std::string& MODEL_ID,
const std::string& TMNL_TYPE,

View File

@@ -687,7 +687,7 @@ bool update_qvvr_file_download(const std::string& filename_with_mac, const std::
//上送文件列表接口
bool send_file_list(terminal_dev* dev, const std::vector<tag_dir_info> &FileList);
std::string getfilefromweb(const std::string& devid, const std::string& remote_path);
//提取mac
std::string normalize_mac(const std::string& mac);

View File

@@ -92,16 +92,35 @@ std::string build_debug_key(const std::string& id, const std::string& level, int
// 递归创建目录
bool create_directory_recursive(const std::string& path) {
size_t pos = 0;
bool create_directory_recursive(const std::string& path)
{
if (path.empty()) return false;
std::string current;
while (pos != std::string::npos) {
pos = path.find('/', pos + 1);
current = path.substr(0, pos);
if (!current.empty() && access(current.c_str(), F_OK) != 0) {
if (mkdir(current.c_str(), 0755) != 0) {
perror(("mkdir failed: " + current).c_str());
return false;
current.reserve(path.size());
for (size_t i = 0; i < path.size(); ++i) {
current += path[i];
// 遇到 '/' 或最后一个字符时创建
if (path[i] == '/' || i == path.size() - 1) {
if (current.empty()) continue;
// 去掉末尾 '/'
std::string dir = current;
if (dir.back() == '/' && dir.size() > 1) {
dir.pop_back();
}
struct stat st;
if (stat(dir.c_str(), &st) != 0) {
if (mkdir(dir.c_str(), 0755) != 0) {
// 如果已经存在(并发场景),忽略
if (errno != EEXIST) {
perror(("mkdir failed: " + dir).c_str());
return false;
}
}
}
}
}

View File

@@ -97,6 +97,8 @@ void update_log_entries_countdown();
void refresh_log_level_cache_locked();
bool create_directory_recursive(const std::string& path);
extern "C" {
#endif
void remove_loggers_by_terminal_id(const std::string& terminal_id_cstr);

View File

@@ -2222,6 +2222,8 @@ bool parsemsg(const std::string& devid, const std::string& guid, const nlohmann:
try {
switch (parsed.type) {
case 1101: { // 读取目录
if (!msgObj.contains("Name") || !msgObj["Name"].is_string()) return false;
int ret = recordguid(devid,guid,static_cast<int>(DeviceState::READING_FILEMENU),1);
if(-1 == ret){ //0记录成功往下执行1装置正忙-1未找到装置
send_reply_to_queue(guid, static_cast<int>(ResponseCode::NOT_FOUND), //响应
@@ -2240,7 +2242,6 @@ bool parsemsg(const std::string& devid, const std::string& guid, const nlohmann:
DIY_INFOLOG_CODE(devid,1,LOG_CODE_CLOUD, "记录装置状态成功,准备读取目录");
}
if (!msgObj.contains("Name") || !msgObj["Name"].is_string()) return false;
parsed.name = msgObj["Name"].get<std::string>();
parsed.ok = true;
@@ -2252,7 +2253,7 @@ bool parsemsg(const std::string& devid, const std::string& guid, const nlohmann:
}
case 1102: { // 下载文件
if (!msgObj.contains("Name") || !msgObj["Name"].is_string()) return false;
{
int ret = recordguid(devid, guid, static_cast<int>(DeviceState::READING_FILEDATA), 1);
if (-1 == ret) {
@@ -2273,7 +2274,7 @@ bool parsemsg(const std::string& devid, const std::string& guid, const nlohmann:
}
}
if (!msgObj.contains("Name") || !msgObj["Name"].is_string()) return false;
parsed.name = msgObj["Name"].get<std::string>();
parsed.ok = true;
@@ -2625,7 +2626,200 @@ bool parsemsg(const std::string& devid, const std::string& guid, const nlohmann:
}
////////lnk20260312新增读取运行状态、版本、对时、重启
////////lnk20260402新增目录新建、删除文件删除文件上传
case 1116: { // 文件上送
if (!msgObj.contains("Name") || !msgObj["Name"].is_string()) return false;
if (!msgObj.contains("RemoteName") || !msgObj["RemoteName"].is_string()) return false;
parsed.ok = true;
std::cout << "[parsemsg] send file, devid=" << devid
<< ", guid=" << guid << std::endl;
//【1】recordguid
{
int ret = recordguid(devid, guid, static_cast<int>(DeviceState::SEND_FILE), 1);
if (-1 == ret) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::NOT_FOUND),
"未找到该装置,文件上送指令执行失败");
DIY_ERRORLOG_CODE(devid, 1, LOG_CODE_CLOUD, "未找到该装置,文件上送指令执行失败");
return true;
}
else if (ret > 0) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::BUSY),
"该装置正忙,文件上送指令执行失败");
DIY_WARNLOG_CODE(devid, 1, LOG_CODE_CLOUD, "该装置正忙,文件上送指令执行失败");
return true;
}
else {
std::cout << "记录装置状态成功,准备执行文件上送" << std::endl;
DIY_INFOLOG_CODE(devid, 1, LOG_CODE_CLOUD, "记录装置状态成功,准备执行文件上送");
}
}
//【2】取参数加 sanitize
std::string remote_path = sanitize(msgObj["Name"].get<std::string>()); // 云端路径
std::string dest_file_path = sanitize(msgObj["RemoteName"].get<std::string>()); // 装置路径
std::cout << "[parsemsg][1116] remote=" << remote_path
<< ", dest=" << dest_file_path << std::endl;
//【3】先下载到本地
std::string local_path = getfilefromweb(devid, remote_path);
if (local_path.empty()) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::BAD_REQUEST),
"文件上送失败,下载源文件失败: " + remote_path);
DIY_ERRORLOG_CODE(devid, 1, LOG_CODE_CLOUD, "文件发送装置失败,下载源文件失败");
return true;
}
//【4】读取本地文件
std::ifstream in(local_path.c_str(), std::ios::in | std::ios::binary);
if (!in.is_open()) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::BAD_REQUEST),
"文件上送失败,无法打开本地文件: " + local_path);
DIY_ERRORLOG_CODE(devid, 1, LOG_CODE_CLOUD, "文件发送装置失败,无法打开本地文件");
return true;
}
std::vector<unsigned char> file_data;
in.seekg(0, std::ios::end);
std::streamoff file_size = in.tellg();
in.seekg(0, std::ios::beg);
if (file_size < 0) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::BAD_REQUEST),
"文件上送失败,读取文件大小异常: " + local_path);
DIY_ERRORLOG_CODE(devid, 1, LOG_CODE_CLOUD, "文件发送装置失败,读取文件大小异常");
return true;
}
file_data.resize(static_cast<size_t>(file_size));
if (file_size > 0) {
in.read(reinterpret_cast<char*>(&file_data[0]), file_size);
}
in.close();
//【5】下发到装置
if (!ClientManager::instance().send_file_action_to_device(
devid, file_data, 10240, dest_file_path)) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::BAD_REQUEST),
"文件上送指令下发失败");
DIY_ERRORLOG_CODE(devid, 1, LOG_CODE_CLOUD, "文件上送指令下发失败");
return true;
}
return true;
}
case 1117: { // 文件删除
if (!msgObj.contains("Name") || !msgObj["Name"].is_string()) return false;
parsed.ok = true;
std::cout << "[parsemsg] delete file, devid=" << devid
<< ", guid=" << guid << std::endl;
{
int ret = recordguid(devid, guid, static_cast<int>(DeviceState::DEL_FILE), 1);
if (-1 == ret) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::NOT_FOUND),
"未找到该装置,文件删除指令执行失败");
DIY_ERRORLOG_CODE(devid, 1, LOG_CODE_CLOUD, "未找到该装置,文件删除指令执行失败");
return true;
}
else if (ret > 0) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::BUSY),
"该装置正忙,文件删除指令执行失败");
DIY_WARNLOG_CODE(devid, 1, LOG_CODE_CLOUD, "该装置正忙,文件删除指令执行失败");
return true;
}
else {
std::cout << "记录装置状态成功,准备执行文件删除" << std::endl;
DIY_INFOLOG_CODE(devid, 1, LOG_CODE_CLOUD, "记录装置状态成功,准备执行文件删除");
}
}
std::string dev_file_name = msgObj["Name"].get<std::string>();
std::cout << "[parsemsg][1117] Name=" << dev_file_name << std::endl;
ClientManager::instance().add_file_delete_action_to_device(devid, dev_file_name);
return true;
}
case 1118: { // 目录创建
if (!msgObj.contains("Name") || !msgObj["Name"].is_string()) return false;
parsed.ok = true;
std::cout << "[parsemsg] create menu, devid=" << devid
<< ", guid=" << guid << std::endl;
{
int ret = recordguid(devid, guid, static_cast<int>(DeviceState::SEND_MENU), 1);
if (-1 == ret) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::NOT_FOUND),
"未找到该装置,目录创建指令执行失败");
DIY_ERRORLOG_CODE(devid, 1, LOG_CODE_CLOUD, "未找到该装置,目录创建指令执行失败");
return true;
}
else if (ret > 0) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::BUSY),
"该装置正忙,目录创建指令执行失败");
DIY_WARNLOG_CODE(devid, 1, LOG_CODE_CLOUD, "该装置正忙,目录创建指令执行失败");
return true;
}
else {
std::cout << "记录装置状态成功,准备执行目录创建" << std::endl;
DIY_INFOLOG_CODE(devid, 1, LOG_CODE_CLOUD, "记录装置状态成功,准备执行目录创建");
}
}
std::string dir_name = msgObj["Name"].get<std::string>();
std::cout << "[parsemsg][1118] Name=" << dir_name << std::endl;
ClientManager::instance().add_menu_set_action_to_device(devid, dir_name);
return true;
}
case 1119: { // 目录删除
if (!msgObj.contains("Name") || !msgObj["Name"].is_string()) return false;
parsed.ok = true;
std::cout << "[parsemsg] delete menu, devid=" << devid
<< ", guid=" << guid << std::endl;
{
int ret = recordguid(devid, guid, static_cast<int>(DeviceState::DEL_MENU), 1);
if (-1 == ret) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::NOT_FOUND),
"未找到该装置,目录删除指令执行失败");
DIY_ERRORLOG_CODE(devid, 1, LOG_CODE_CLOUD, "未找到该装置,目录删除指令执行失败");
return true;
}
else if (ret > 0) {
send_reply_to_queue(guid, static_cast<int>(ResponseCode::BUSY),
"该装置正忙,目录删除指令执行失败");
DIY_WARNLOG_CODE(devid, 1, LOG_CODE_CLOUD, "该装置正忙,目录删除指令执行失败");
return true;
}
else {
std::cout << "记录装置状态成功,准备执行目录删除" << std::endl;
DIY_INFOLOG_CODE(devid, 1, LOG_CODE_CLOUD, "记录装置状态成功,准备执行目录删除");
}
}
std::string dir_name = msgObj["Name"].get<std::string>();
std::cout << "[parsemsg][1119] Name=" << dir_name << std::endl;
ClientManager::instance().add_menu_del_action_to_device(devid, dir_name);
return true;
}
////////lnk20260402新增目录新建、删除文件删除文件上传
default:
return false;
}

View File

@@ -904,6 +904,19 @@ void process_received_message(string mac, string id,const char* data, size_t len
// 处理完成后重置状态
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewNACK)) {
// 文件目录为空
std::cout << "*** empty ***! " << mac << std::endl;
//传入空的list表示目录为空
std::vector<tag_dir_info> FileList;
filemenu_cache_put(id,FileList);
on_device_response_minimal(static_cast<int>(ResponseCode::OK), id, 0, static_cast<int>(DeviceState::READING_FILEMENU));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
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;
// 装置答非所问异常
@@ -918,6 +931,9 @@ void process_received_message(string mac, string id,const char* data, size_t len
if(udata[8] == static_cast<unsigned char>(MsgResponseType::Response_File_Send)){
//文件应答最后一帧后,再回复的结束帧
std::cout << "*** send file success ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::OK), id, 0, static_cast<int>(DeviceState::SEND_FILE));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewACK)) {
@@ -937,6 +953,9 @@ void process_received_message(string mac, string id,const char* data, size_t len
if (!ok) {
// 组装后续文件上送报文失败
std::cout << "*** send file get next packet fail ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::BAD_REQUEST), id, 0, static_cast<int>(DeviceState::SEND_FILE));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else if (!packet.empty()) {
@@ -959,17 +978,26 @@ void process_received_message(string mac, string id,const char* data, size_t len
else {
// 理论上不应出现
std::cout << "*** send file invalid next packet ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::INTERNAL_ERROR), id, 0, static_cast<int>(DeviceState::SEND_FILE));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
}
else if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewNACK)) {
// 当前帧被拒收,文件上送失败
std::cout << "*** send file 0x41 fail ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::REJECTED_BUSY), id, 0, static_cast<int>(DeviceState::SEND_FILE));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问
std::cout << "*** send file ?? fail ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::INTERNAL_ERROR), id, 0, static_cast<int>(DeviceState::SEND_FILE));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
@@ -979,16 +1007,25 @@ void process_received_message(string mac, string id,const char* data, size_t len
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewACK)) {
//文件删除完毕!
std::cout << "*** del file success ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::OK), id, 0, static_cast<int>(DeviceState::DEL_FILE));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewNACK)) {
// 当前帧被拒收,文件删除失败
std::cout << "*** del file 0x41 fail ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::REJECTED_BUSY), id, 0, static_cast<int>(DeviceState::DEL_FILE));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问
std::cout << "*** del file ?? fail ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::INTERNAL_ERROR), id, 0, static_cast<int>(DeviceState::DEL_FILE));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
@@ -998,16 +1035,25 @@ void process_received_message(string mac, string id,const char* data, size_t len
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewACK)) {
//创建目录完毕!
std::cout << "*** send menu success ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::OK), id, 0, static_cast<int>(DeviceState::SEND_MENU));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewNACK)) {
// 当前帧被拒收,创建目录失败
std::cout << "*** send menu 0x41 fail ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::REJECTED_BUSY), id, 0, static_cast<int>(DeviceState::SEND_MENU));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问
std::cout << "*** send menu ?? fail ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::INTERNAL_ERROR), id, 0, static_cast<int>(DeviceState::SEND_MENU));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;
@@ -1017,16 +1063,25 @@ void process_received_message(string mac, string id,const char* data, size_t len
if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewACK)) {
//删除目录完毕!
std::cout << "*** del menu success ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::OK), id, 0, static_cast<int>(DeviceState::DEL_MENU));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else if (udata[8] == static_cast<unsigned char>(MsgResponseType::Response_NewNACK)) {
// 当前帧被拒收,删除目录失败
std::cout << "*** del menu 0x41 fail ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::REJECTED_BUSY), id, 0, static_cast<int>(DeviceState::DEL_MENU));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
else {
// 装置答非所问
std::cout << "*** del menu ?? fail ***! " << mac << std::endl;
on_device_response_minimal(static_cast<int>(ResponseCode::INTERNAL_ERROR), id, 0, static_cast<int>(DeviceState::DEL_MENU));
ClientManager::instance().change_device_state(id, DeviceState::IDLE);
}
break;