chenluhua1980
7 天以前 ceb64b6612309fe384e096dcdc8b5a5e0dfe6cce
SourceCode/Bond/Servo/RecipeManager.cpp
@@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include "RecipeManager.h"
#include <sstream>
#include <iomanip>
@@ -8,411 +8,453 @@
std::recursive_mutex RecipeManager::m_mutex;
RecipeManager& RecipeManager::getInstance() {
    static RecipeManager instance;
    return instance;
   static RecipeManager instance;
   return instance;
}
RecipeManager::RecipeManager() {
    m_pDB = new BL::SQLiteDatabase();
   m_pDB = new BL::SQLiteDatabase();
}
RecipeManager::~RecipeManager() {
    if (m_pDB) {
        delete m_pDB;
        m_pDB = nullptr;
    }
   if (m_pDB) {
      delete m_pDB;
      m_pDB = nullptr;
   }
}
bool RecipeManager::initRecipeTable() {
    char szPath[MAX_PATH];
    GetModuleFileNameA(NULL, szPath, MAX_PATH);
    std::string exePath(szPath);
    std::string dbDir = exePath.substr(0, exePath.find_last_of("\\/")) + "\\DB";
    CreateDirectoryA(dbDir.c_str(), NULL);
   char szPath[MAX_PATH];
   GetModuleFileNameA(NULL, szPath, MAX_PATH);
   std::string exePath(szPath);
   std::string dbDir = exePath.substr(0, exePath.find_last_of("\\/")) + "\\DB";
   CreateDirectoryA(dbDir.c_str(), NULL);
    std::string dbPath = dbDir + "\\RecipeManager.db";
    if (!m_pDB->connect(dbPath, true)) {
        return false;
    }
   std::string dbPath = dbDir + "\\RecipeManager.db";
   if (!m_pDB->connect(dbPath, true)) {
      return false;
   }
    // 启用 SQLite 的外键约束支持
    if (!m_pDB->executeQuery("PRAGMA foreign_keys = ON;")) {
        std::cerr << "Failed to enable foreign keys." << std::endl;
        return false;
    }
   // 启用 SQLite 的外键约束支持
   if (!m_pDB->executeQuery("PRAGMA foreign_keys = ON;")) {
      std::cerr << "Failed to enable foreign keys." << std::endl;
      return false;
   }
    const std::string createRecipeTable = R"(
        CREATE TABLE IF NOT EXISTS recipes (
            ppid TEXT PRIMARY KEY NOT NULL,
            description TEXT,
            create_time TEXT DEFAULT (datetime('now', 'localtime'))
        );
    )";
   const std::string createRecipeTable = R"(
      CREATE TABLE IF NOT EXISTS recipes (
         id INTEGER PRIMARY KEY AUTOINCREMENT,
         ppid TEXT NOT NULL UNIQUE,
         description TEXT,
         create_time TEXT DEFAULT (datetime('now', 'localtime'))
      );
   )";
    const std::string createDeviceTable = R"(
        CREATE TABLE IF NOT EXISTS recipe_devices (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            ppid TEXT NOT NULL,
            device_id INTEGER NOT NULL,
            device_name TEXT NOT NULL,
            recipe_id INTEGER NOT NULL,
            FOREIGN KEY(ppid) REFERENCES recipes(ppid) ON DELETE CASCADE ON UPDATE CASCADE,
            UNIQUE (ppid, device_id),
            UNIQUE (ppid, device_name)
        );
    )";
   const std::string createDeviceTable = R"(
      CREATE TABLE IF NOT EXISTS recipe_devices (
         id INTEGER PRIMARY KEY AUTOINCREMENT,
         ppid TEXT NOT NULL,
         device_id INTEGER NOT NULL,
         device_name TEXT NOT NULL,
         recipe_id INTEGER NOT NULL,
         recipe_name TEXT NOT NULL,
         FOREIGN KEY(ppid) REFERENCES recipes(ppid) ON DELETE CASCADE ON UPDATE CASCADE,
         UNIQUE (ppid, device_id),
         UNIQUE (ppid, device_name)
      );
   )";
    return m_pDB->executeQuery(createRecipeTable)
        && m_pDB->executeQuery(createDeviceTable);
   return m_pDB->executeQuery(createRecipeTable) && m_pDB->executeQuery(createDeviceTable);
}
void RecipeManager::termRecipeTable() {
    if (!m_pDB) {
        return;
    }
   if (!m_pDB) {
      return;
   }
    m_pDB->disconnect();
   m_pDB->disconnect();
}
bool RecipeManager::destroyRecipeTable() {
    if (!m_pDB) {
   if (!m_pDB) {
      return false;
    }
   }
    return m_pDB->executeQuery("DROP TABLE IF EXISTS recipe_devices;") && m_pDB->executeQuery("DROP TABLE IF EXISTS recipes;");
   return m_pDB->executeQuery("DROP TABLE IF EXISTS recipe_devices;") && m_pDB->executeQuery("DROP TABLE IF EXISTS recipes;");
}
bool RecipeManager::ppidExists(const std::string& ppid) {
    std::ostringstream oss;
    oss << "SELECT COUNT(*) FROM recipes WHERE ppid = '" << ppid << "';";
    auto result = m_pDB->fetchResults(oss.str());
    return (!result.empty() && !result[0].empty() && result[0][0] != "0");
   std::ostringstream oss;
   oss << "SELECT COUNT(*) FROM recipes WHERE ppid = '" << ppid << "';";
   auto result = m_pDB->fetchResults(oss.str());
   return (!result.empty() && !result[0].empty() && result[0][0] != "0");
}
bool RecipeManager::deviceExists(const std::string& ppid, int nDeviceID) {
    std::ostringstream oss;
    oss << "SELECT COUNT(*) FROM recipe_devices WHERE ppid = '" << ppid
        << "' AND device_id = " << nDeviceID << ";";
    auto result = m_pDB->fetchResults(oss.str());
    return (!result.empty() && !result[0].empty() && result[0][0] != "0");
   std::ostringstream oss;
   oss << "SELECT COUNT(*) FROM recipe_devices WHERE ppid = '" << ppid
      << "' AND device_id = " << nDeviceID << ";";
   auto result = m_pDB->fetchResults(oss.str());
   return (!result.empty() && !result[0].empty() && result[0][0] != "0");
}
bool RecipeManager::addRecipe(const RecipeInfo& recipe) {
    if (!m_pDB || recipe.strPPID.empty() || recipe.vecDeviceList.empty()) {
        std::cerr << "[AddRecipe] Invalid input." << std::endl;
        return false;
    }
   if (!m_pDB || recipe.strPPID.empty() || recipe.vecDeviceList.empty()) {
      std::cerr << "[AddRecipe] Invalid input." << std::endl;
      return false;
   }
    std::string strTime = recipe.strCreateTime;
    if (strTime.empty()) {
        std::time_t now = std::time(nullptr);
        std::tm tm_now = {};
        localtime_s(&tm_now, &now);
        std::stringstream ss;
        ss << std::put_time(&tm_now, "%Y-%m-%d %H:%M:%S");
        strTime = ss.str();
    }
   std::string strTime = recipe.strCreateTime;
   if (strTime.empty()) {
      std::time_t now = std::time(nullptr);
      std::tm tm_now = {};
      localtime_s(&tm_now, &now);
      std::stringstream ss;
      ss << std::put_time(&tm_now, "%Y-%m-%d %H:%M:%S");
      strTime = ss.str();
   }
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
   std::lock_guard<std::recursive_mutex> lock(m_mutex);
    // 开始事务
    m_pDB->executeQuery("BEGIN TRANSACTION;");
   // 开始事务
   m_pDB->executeQuery("BEGIN TRANSACTION;");
    std::ostringstream oss;
    oss << "INSERT OR REPLACE INTO recipes (ppid, description, create_time) VALUES ('"
        << recipe.strPPID << "', '"
        << recipe.strDescription << "', '"
        << strTime << "');";
   std::ostringstream oss;
   oss << "INSERT OR REPLACE INTO recipes (ppid, description, create_time) VALUES ('"
      << recipe.strPPID << "', '"
      << recipe.strDescription << "', '"
      << strTime << "');";
    if (!m_pDB->executeQuery(oss.str())) {
        std::cerr << "[AddRecipe] Failed to insert recipe: " << recipe.strPPID << std::endl;
        m_pDB->executeQuery("ROLLBACK;");
        return false;
    }
   if (!m_pDB->executeQuery(oss.str())) {
      std::cerr << "[AddRecipe] Failed to insert recipe: " << recipe.strPPID << std::endl;
      m_pDB->executeQuery("ROLLBACK;");
      return false;
   }
    for (const auto& device : recipe.vecDeviceList) {
        std::ostringstream devSql;
        devSql << "INSERT OR REPLACE INTO recipe_devices (ppid, device_id, device_name, recipe_id) VALUES ('"
            << recipe.strPPID << "', "
            << device.nDeviceID << ", '"
            << device.strDeviceName << "', "
            << device.nRecipeID << ");";
   for (const auto& device : recipe.vecDeviceList) {
      std::ostringstream devSql;
      devSql << "INSERT OR REPLACE INTO recipe_devices (ppid, device_id, device_name, recipe_id, recipe_name) VALUES ('"
         << recipe.strPPID << "', "
         << device.nDeviceID << ", '"
         << device.strDeviceName << "', "
         << device.nRecipeID << ", '"
         << device.strRecipeName << "');";
        if (!m_pDB->executeQuery(devSql.str())) {
            std::cerr << "[AddRecipe] Failed to insert device mapping: " << device.nDeviceID << std::endl;
            m_pDB->executeQuery("ROLLBACK;");
            return false;
        }
    }
    // 提交事务
    m_pDB->executeQuery("COMMIT;");
    return true;
      if (!m_pDB->executeQuery(devSql.str())) {
         std::cerr << "[AddRecipe] Failed to insert device mapping: " << device.nDeviceID << std::endl;
         m_pDB->executeQuery("ROLLBACK;");
         return false;
      }
   }
   // 提交事务
   m_pDB->executeQuery("COMMIT;");
   return true;
}
bool RecipeManager::addRecipeDevice(const std::string& ppid, const DeviceRecipe& device) {
    if (!m_pDB || ppid.empty() || device.nDeviceID <= 0 || device.nRecipeID <= 0) {
        std::cerr << "[addRecipeDevice] Invalid input." << std::endl;
        return false;
    }
   if (!m_pDB || ppid.empty() || device.nDeviceID <= 0 || device.nRecipeID <= 0) {
      std::cerr << "[addRecipeDevice] Invalid input." << std::endl;
      return false;
   }
    // 检查 ppid 是否存在
   // 检查 ppid 是否存在
   if (!ppidExists(ppid)) {
      std::cerr << "[addRecipeDevice] PPID does not exist: " << ppid << std::endl;
      return false;
   }
    // 插入设备记录
    std::ostringstream oss;
    oss << "INSERT OR REPLACE INTO recipe_devices (ppid, device_id, device_name, recipe_id) VALUES ('"
        << ppid << "', "
        << device.nDeviceID << ", '"
        << device.strDeviceName << "', "
        << device.nRecipeID << ");";
   // 插入设备记录
   std::ostringstream oss;
   oss << "INSERT OR REPLACE INTO recipe_devices (ppid, device_id, device_name, recipe_id, recipe_name) VALUES ('"
      << ppid << "', "
      << device.nDeviceID << ", '"
      << device.strDeviceName << "', "
      << device.nRecipeID << ", '"
      << device.strRecipeName << "');";
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    return m_pDB->executeQuery(oss.str());
   std::lock_guard<std::recursive_mutex> lock(m_mutex);
   return m_pDB->executeQuery(oss.str());
}
bool RecipeManager::deleteRecipeDeviceByID(const std::string& ppid, int nDeviceID) {
    if (!m_pDB || ppid.empty() || nDeviceID <= 0) {
        std::cerr << "[deleteRecipeDeviceByID] Invalid input." << std::endl;
        return false;
    }
   if (!m_pDB || ppid.empty() || nDeviceID <= 0) {
      std::cerr << "[deleteRecipeDeviceByID] Invalid input." << std::endl;
      return false;
   }
    std::ostringstream oss;
    oss << "DELETE FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_id = " << nDeviceID << ";";
   std::ostringstream oss;
   oss << "DELETE FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_id = " << nDeviceID << ";";
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    return m_pDB->executeQuery(oss.str());
   std::lock_guard<std::recursive_mutex> lock(m_mutex);
   return m_pDB->executeQuery(oss.str());
}
bool RecipeManager::deleteRecipeDeviceByName(const std::string& ppid, const std::string& strDeviceName) {
    if (!m_pDB || ppid.empty() || strDeviceName.empty()) {
        std::cerr << "[deleteRecipeDeviceByName] Invalid input." << std::endl;
        return false;
    }
   if (!m_pDB || ppid.empty() || strDeviceName.empty()) {
      std::cerr << "[deleteRecipeDeviceByName] Invalid input." << std::endl;
      return false;
   }
    std::ostringstream oss;
    oss << "DELETE FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_name = '" << strDeviceName << "';";
   std::ostringstream oss;
   oss << "DELETE FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_name = '" << strDeviceName << "';";
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    return m_pDB->executeQuery(oss.str());
   std::lock_guard<std::recursive_mutex> lock(m_mutex);
   return m_pDB->executeQuery(oss.str());
}
std::vector<RecipeInfo> RecipeManager::getAllRecipes() {
    if (!m_pDB) {
        return {};
    }
   if (!m_pDB) {
      return {};
   }
    std::vector<RecipeInfo> recipes;
    auto rows = m_pDB->fetchResults("SELECT ppid, description, create_time FROM recipes;");
   std::vector<RecipeInfo> recipes;
   auto rows = m_pDB->fetchResults("SELECT ppid, description, create_time FROM recipes;");
    for (const auto& row : rows) {
        RecipeInfo info;
        info.strPPID = row[0];
        info.strDescription = row[1];
        info.strCreateTime = row[2];
   for (const auto& row : rows) {
      RecipeInfo info;
      info.strPPID = row[0];
      info.strDescription = row[1];
      info.strCreateTime = row[2];
        std::ostringstream devQuery;
        devQuery << "SELECT device_id, device_name, recipe_id FROM recipe_devices WHERE ppid = '" << info.strPPID << "';";
        auto devs = m_pDB->fetchResults(devQuery.str());
      std::ostringstream devQuery;
      devQuery << "SELECT device_id, device_name, recipe_id, recipe_name FROM recipe_devices WHERE ppid = '" << info.strPPID << "'ORDER BY id ASC;";
      auto devs = m_pDB->fetchResults(devQuery.str());
        for (const auto& dev : devs) {
            DeviceRecipe dr;
            dr.strPPID = info.strPPID;
            try {
                dr.nDeviceID = std::stoi(dev[0]);
                dr.strDeviceName = dev[1];
                dr.nRecipeID = std::stoi(dev[2]);
            }
            catch (...) {
                std::cerr << "Invalid data in recipe_devices for PPID: " << info.strPPID << std::endl;
                continue;
            }
            info.vecDeviceList.push_back(dr);
        }
        recipes.push_back(info);
    }
      for (const auto& dev : devs) {
         DeviceRecipe dr;
         try {
            dr.nDeviceID = std::stoi(dev[0]);
            dr.strDeviceName = dev[1];
            dr.nRecipeID = std::stoi(dev[2]);
            dr.strRecipeName = dev[3];
         }
         catch (...) {
            std::cerr << "Invalid data in recipe_devices for PPID: " << info.strPPID << std::endl;
            continue;
         }
         info.vecDeviceList.push_back(dr);
      }
      recipes.push_back(info);
   }
    return recipes;
   return recipes;
}
std::vector<RecipeInfo> RecipeManager::getRecipesByKeyword(const std::string& keyword) {
    std::vector<RecipeInfo> recipes;
    if (!m_pDB || keyword.empty()) {
        return recipes;
    }
   std::vector<RecipeInfo> recipes;
   if (!m_pDB || keyword.empty()) {
      return recipes;
   }
    std::ostringstream query;
    query << "SELECT ppid, description, create_time FROM recipes "
        << "WHERE ppid LIKE '%" << keyword << "%' OR description LIKE '%" << keyword << "%';";
   std::ostringstream query;
   query << "SELECT ppid, description, create_time FROM recipes "
      << "WHERE ppid LIKE '%" << keyword << "%' OR description LIKE '%" << keyword << "%';";
    auto rows = m_pDB->fetchResults(query.str());
    for (const auto& row : rows) {
        if (row.size() >= 3) {
            RecipeInfo info;
            info.strPPID = row[0];
            info.strDescription = row[1];
            info.strCreateTime = row[2];
            recipes.push_back(info);
        }
    }
    return recipes;
   auto rows = m_pDB->fetchResults(query.str());
   for (const auto& row : rows) {
      if (row.size() >= 3) {
         RecipeInfo info;
         info.strPPID = row[0];
         info.strDescription = row[1];
         info.strCreateTime = row[2];
         recipes.push_back(info);
      }
   }
   return recipes;
}
std::vector<std::string> RecipeManager::getAllPPID() const {
    std::vector<std::string> vecPPID;
   std::vector<std::string> vecPPID;
    if (!m_pDB) {
        return vecPPID;
    }
   if (!m_pDB) {
      return vecPPID;
   }
    const std::string query = "SELECT ppid FROM recipes ORDER BY ppid;";
    auto result = m_pDB->fetchResults(query);
   const std::string query = "SELECT ppid FROM recipes ORDER BY ppid;";
   auto result = m_pDB->fetchResults(query);
    for (const auto& row : result) {
        if (!row.empty()) {
            vecPPID.push_back(row[0]);
        }
    }
   for (const auto& row : result) {
      if (!row.empty()) {
         vecPPID.push_back(row[0]);
      }
   }
    return vecPPID;
   return vecPPID;
}
std::string RecipeManager::getPPIDById(int nId) {
   if (!m_pDB) {
      return {};
   }
   std::ostringstream query;
   query << "SELECT ppid FROM recipes WHERE id = " << nId << ";";
   auto rows = m_pDB->fetchResults(query.str());
   if (rows.empty() || rows[0].empty()) {
      return {};
   }
   return rows[0][0];
}
int RecipeManager::getIdByPPID(const std::string& ppid) {
   if (!m_pDB) {
      return -1;
   }
   std::ostringstream query;
   query << "SELECT id FROM recipes WHERE ppid = '" << ppid << "';";
   auto rows = m_pDB->fetchResults(query.str());
   if (rows.empty() || rows[0].empty()) {
      return -1;
   }
   try {
      return std::stoi(rows[0][0]);
   }
   catch (...) {
      std::cerr << "Invalid id value for PPID: " << ppid << std::endl;
      return -1;
   }
}
RecipeInfo RecipeManager::getRecipeByPPID(const std::string& ppid) {
    RecipeInfo info;
    auto rows = m_pDB->fetchResults("SELECT ppid, description, create_time FROM recipes WHERE ppid = '" + ppid + "';");
    if (rows.empty()) {
        return info;
    }
   RecipeInfo info;
   auto rows = m_pDB->fetchResults("SELECT ppid, description, create_time FROM recipes WHERE ppid = '" + ppid + "';");
   if (rows.empty()) {
      return info;
   }
    info.strPPID = rows[0][0];
    info.strDescription = rows[0][1];
    info.strCreateTime = rows[0][2];
   info.strPPID = rows[0][0];
   info.strDescription = rows[0][1];
   info.strCreateTime = rows[0][2];
    auto devs = m_pDB->fetchResults("SELECT device_id, device_name, recipe_id FROM recipe_devices WHERE ppid = '" + ppid + "';");
    for (const auto& dev : devs) {
        DeviceRecipe dr;
        dr.strPPID = ppid;
        try {
            dr.nDeviceID = std::stoi(dev[0]);
            dr.strDeviceName = dev[1];
            dr.nRecipeID = std::stoi(dev[2]);
        }
        catch (...) {
            std::cerr << "Invalid data in recipe_devices for PPID: " << ppid << std::endl;
            continue;
        }
        info.vecDeviceList.push_back(dr);
    }
    return info;
   auto devs = m_pDB->fetchResults("SELECT device_id, device_name, recipe_id, recipe_name FROM recipe_devices WHERE ppid = '" + ppid + "';");
   for (const auto& dev : devs) {
      DeviceRecipe dr;
      try {
         dr.nDeviceID = std::stoi(dev[0]);
         dr.strDeviceName = dev[1];
         dr.nRecipeID = std::stoi(dev[2]);
         dr.strRecipeName = dev[3];
      }
      catch (...) {
         std::cerr << "Invalid data in recipe_devices for PPID: " << ppid << std::endl;
         continue;
      }
      info.vecDeviceList.push_back(dr);
   }
   return info;
}
int RecipeManager::getDeviceRecipeIDByID(const std::string& ppid, int nDeviceID) {
    if (!m_pDB || ppid.empty() || nDeviceID <= 0) {
        return -1;
    }
   if (!m_pDB || ppid.empty() || nDeviceID <= 0) {
      return -1;
   }
    std::ostringstream query;
    query << "SELECT recipe_id FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_id = " << nDeviceID << ";";
   std::ostringstream query;
   query << "SELECT recipe_id FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_id = " << nDeviceID << ";";
    auto result = m_pDB->fetchResults(query.str());
    if (!result.empty() && !result[0].empty()) {
        try {
            return std::stoi(result[0][0]);
        }
        catch (...) {
            return -1;
        }
    }
    return -1;
   auto result = m_pDB->fetchResults(query.str());
   if (!result.empty() && !result[0].empty()) {
      try {
         return std::stoi(result[0][0]);
      }
      catch (...) {
         return -1;
      }
   }
   return -1;
}
int RecipeManager::getDeviceRecipeIDByName(const std::string& ppid, const std::string& strDeviceName) {
    if (!m_pDB || ppid.empty() || strDeviceName.empty()) {
        return -1;
    }
   if (!m_pDB || ppid.empty() || strDeviceName.empty()) {
      return -1;
   }
    std::ostringstream query;
    query << "SELECT recipe_id FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_name = '" << strDeviceName << "';";
   std::ostringstream query;
   query << "SELECT recipe_id FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_name = '" << strDeviceName << "';";
    auto result = m_pDB->fetchResults(query.str());
    if (!result.empty() && !result[0].empty()) {
        try {
            return std::stoi(result[0][0]);
        }
        catch (...) {
            return -1;
        }
    }
    return -1;
   auto result = m_pDB->fetchResults(query.str());
   if (!result.empty() && !result[0].empty()) {
      try {
         return std::stoi(result[0][0]);
      }
      catch (...) {
         return -1;
      }
   }
   return -1;
}
bool RecipeManager::deleteRecipeByPPID(const std::string& ppid) {
    if (!m_pDB) {
        return false;
    }
   if (!m_pDB) {
      return false;
   }
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    return m_pDB->executeQuery("DELETE FROM recipes WHERE ppid = '" + ppid + "';");
   std::lock_guard<std::recursive_mutex> lock(m_mutex);
   return m_pDB->executeQuery("DELETE FROM recipes WHERE ppid = '" + ppid + "';");
}
bool RecipeManager::updateRecipe(const RecipeInfo& recipe) {
    if (!m_pDB) {
        return false;
    }
   if (!m_pDB) {
      return false;
   }
   if (recipe.strPPID.empty()) {
      std::cerr << "Recipe PPID cannot be empty." << std::endl;
      return false;
   }
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    deleteRecipeByPPID(recipe.strPPID);
    return addRecipe(recipe);
   std::lock_guard<std::recursive_mutex> lock(m_mutex);
   deleteRecipeByPPID(recipe.strPPID);
   return addRecipe(recipe);
}
bool RecipeManager::updatePPID(const std::string& oldPPID, const std::string& newPPID) {
    if (!m_pDB || oldPPID.empty() || newPPID.empty()) {
        std::cerr << "[updatePPID] Invalid input." << std::endl;
        return false;
    }
   if (!m_pDB || oldPPID.empty() || newPPID.empty()) {
      std::cerr << "[updatePPID] Invalid input." << std::endl;
      return false;
   }
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
   std::lock_guard<std::recursive_mutex> lock(m_mutex);
    // 检查是否已经存在相同的 newPPID
    auto check = m_pDB->fetchResults("SELECT COUNT(*) FROM recipes WHERE ppid = '" + newPPID + "';");
    if (!check.empty() && !check[0].empty() && check[0][0] != "0") {
        std::cerr << "[updatePPID] New PPID already exists: " << newPPID << std::endl;
        return false;
    }
   // 检查是否已经存在相同的 newPPID
   auto check = m_pDB->fetchResults("SELECT COUNT(*) FROM recipes WHERE ppid = '" + newPPID + "';");
   if (!check.empty() && !check[0].empty() && check[0][0] != "0") {
      std::cerr << "[updatePPID] New PPID already exists: " << newPPID << std::endl;
      return false;
   }
    m_pDB->executeQuery("BEGIN TRANSACTION;");
   m_pDB->executeQuery("BEGIN TRANSACTION;");
    std::ostringstream sql;
    sql << "UPDATE recipes SET ppid = '" << newPPID << "' WHERE ppid = '" << oldPPID << "';";
    if (!m_pDB->executeQuery(sql.str())) {
        std::cerr << "[updatePPID] Failed to update recipes table." << std::endl;
        m_pDB->executeQuery("ROLLBACK;");
        return false;
    }
   std::ostringstream sql;
   sql << "UPDATE recipes SET ppid = '" << newPPID << "' WHERE ppid = '" << oldPPID << "';";
   if (!m_pDB->executeQuery(sql.str())) {
      std::cerr << "[updatePPID] Failed to update recipes table." << std::endl;
      m_pDB->executeQuery("ROLLBACK;");
      return false;
   }
    m_pDB->executeQuery("COMMIT;");
    return true;
   m_pDB->executeQuery("COMMIT;");
   return true;
}
bool RecipeManager::updateDescription(const std::string& ppid, const std::string& newDescription) {
    if (!m_pDB || ppid.empty()) {
        std::cerr << "[updateRecipeDescription] Invalid input." << std::endl;
        return false;
    }
   if (!m_pDB || ppid.empty()) {
      std::cerr << "[updateRecipeDescription] Invalid input." << std::endl;
      return false;
   }
    std::ostringstream oss;
    oss << "UPDATE recipes SET description = '" << newDescription << "' WHERE ppid = '" << ppid << "';";
   std::ostringstream oss;
   oss << "UPDATE recipes SET description = '" << newDescription << "' WHERE ppid = '" << ppid << "';";
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    return m_pDB->executeQuery(oss.str());
   std::lock_guard<std::recursive_mutex> lock(m_mutex);
   return m_pDB->executeQuery(oss.str());
}
bool RecipeManager::updateDeviceRecipeIDByID(const std::string& ppid, int nDeviceID, int nNewRecipeID) {
@@ -421,7 +463,7 @@
   }
   std::ostringstream query;
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
   std::lock_guard<std::recursive_mutex> lock(m_mutex);
   query << "UPDATE recipe_devices SET recipe_id = " << nNewRecipeID
      << " WHERE ppid = '" << ppid << "' AND device_id = " << nDeviceID << ";";
   return m_pDB->executeQuery(query.str());
@@ -432,99 +474,225 @@
      return false;
   }
   std::ostringstream query;
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
   std::lock_guard<std::recursive_mutex> lock(m_mutex);
   query << "UPDATE recipe_devices SET recipe_id = " << nNewRecipeID
      << " WHERE ppid = '" << ppid << "' AND device_name = '" << strDeviceName << "';";
   return m_pDB->executeQuery(query.str());
}
bool RecipeManager::addDeviceRecipe(const std::string& strDeviceName, int nID, const std::string& strName, const std::string& strPara) {
   if (!m_pDB || strDeviceName.empty() || nID <= 0 || strName.empty() || strPara.empty()) {
      return false;
   }
   std::ostringstream sql;
   sql << "CREATE TABLE IF NOT EXISTS " << strDeviceName << "_Recipes ("
      << "recipe_id INTEGER PRIMARY KEY,"
      << "recipe_name TEXT NOT NULL,"
      << "recipe_para TEXT NOT NULL"
      << ");";
   bool bRet = m_pDB->executeQuery(sql.str());
   if (!bRet) {
      return false;
   }
   std::ostringstream ins;
   ins << "INSERT OR REPLACE INTO " << strDeviceName
      << "_Recipes (recipe_id, recipe_name, recipe_para) VALUES ("
      << nID << ", '" << strName << "', '" << strPara << "');";
   std::lock_guard<std::recursive_mutex> lk(m_mutex);
   return m_pDB->executeQuery(ins.str());
}
bool RecipeManager::updateDeviceRecipeName(const std::string& strDeviceName, int nID, const std::string& strNewName) {
   if (!m_pDB || strDeviceName.empty() || nID <= 0 || strNewName.empty()) {
      return false;
   }
   std::ostringstream sql;
   sql << "UPDATE " << strDeviceName << "_Recipes SET recipe_name='" << strNewName
      << "' WHERE recipe_id=" << nID << ";";
   std::lock_guard<std::recursive_mutex> lk(m_mutex);
   return m_pDB->executeQuery(sql.str());
}
bool RecipeManager::updateDeviceRecipePara(const std::string& strDeviceName, int nID, const std::string& strNewPara) {
   if (!m_pDB || strDeviceName.empty() || nID <= 0) {
      return false;
   }
   std::ostringstream sql;
   sql << "UPDATE " << strDeviceName << "_Recipes SET recipe_para='" << strNewPara
      << "' WHERE recipe_id=" << nID << ";";
   std::lock_guard<std::recursive_mutex> lk(m_mutex);
   return m_pDB->executeQuery(sql.str());
}
std::string RecipeManager::getDeviceRecipeName(const std::string& strDeviceName, int nID) {
   if (!m_pDB || strDeviceName.empty() || nID <= 0) {
      return "";
   }
   std::ostringstream sql;
   sql << "SELECT recipe_name FROM " << strDeviceName << "_Recipes "
      << "WHERE recipe_id=" << nID << " LIMIT 1;";
   auto rows = m_pDB->fetchResults(sql.str());
   if (!rows.empty() && !rows[0].empty()) {
      return rows[0][0];
   }
   return "";
}
std::string RecipeManager::getDeviceRecipePara(const std::string& strDeviceName, int nID) {
   if (!m_pDB || strDeviceName.empty() || nID <= 0) {
      return "";
   }
   std::ostringstream sql;
   sql << "SELECT recipe_para FROM " << strDeviceName << "_Recipes "
      << "WHERE recipe_id=" << nID << " LIMIT 1;";
   auto rows = m_pDB->fetchResults(sql.str());
   if (!rows.empty() && !rows[0].empty()) {
      return rows[0][0];
   }
   return "";
}
bool RecipeManager::deleteDeviceRecipe(const std::string& strDeviceName, int nID) {
   if (!m_pDB || strDeviceName.empty() || nID <= 0) {
      return false;
   }
   std::ostringstream sql;
   sql << "DELETE FROM " << strDeviceName << "_Recipes WHERE recipe_id=" << nID << ";";
   std::lock_guard<std::recursive_mutex> lk(m_mutex);
   return m_pDB->executeQuery(sql.str());
}
std::vector<std::pair<int, std::string>> RecipeManager::getDeviceRecipes(const std::string& strDeviceName) {
   std::vector<std::pair<int, std::string>> out;
   if (!m_pDB || strDeviceName.empty()) {
      return out;
   }
   std::ostringstream sql;
   sql << "SELECT recipe_id, recipe_name FROM " << strDeviceName << "_Recipes ORDER BY recipe_id;";
   auto rows = m_pDB->fetchResults(sql.str());
   for (const auto& r : rows) {
      if (r.size() < 2) continue;
      try {
         int id = std::stoi(r[0]);
         out.emplace_back(id, r[1]);
      }
      catch (...) {}
   }
   return out;
}
void RecipeManager::insertMockData() {
    if (!m_pDB) {
        return;
    }
   if (!m_pDB) {
      return;
   }
    RecipeInfo recipe;
    recipe.strPPID = "P1001";
    recipe.strDescription = "Main Board Burn-in";
   RecipeInfo recipe;
   recipe.strPPID = "P1001";
   recipe.strDescription = "Main Board Burn-in";
    recipe.vecDeviceList = {
        {1, 101, "P1001","Burner A"},
        {2, 102, "P1001", "Burner B"}
    };
   recipe.vecDeviceList = {
      {9, 101, "VacuumBake", "VacuumBake"},
      {10, 102, "Bonder1", "Bonder1"},
      {11, 103, "Bonder2", "Bonder2"}
   };
    addRecipe(recipe);
   addRecipe(recipe);
   addDeviceRecipe("Bonder1", 101, "标准工艺", "00");
   addDeviceRecipe("Bonder1", 102, "改良工艺", "00");
   addDeviceRecipe("Bonder1", 103, "高速模式", "00");
   addDeviceRecipe("Bonder2", 101, "标准工艺", "00");
   addDeviceRecipe("Bonder2", 102, "改良工艺", "00");
   addDeviceRecipe("Bonder2", 103, "高速模式", "00");
}
bool RecipeManager::readRecipeFile(const std::string& filename) {
    if (!m_pDB) {
        return false;
    }
   if (!m_pDB) {
      return false;
   }
    std::ifstream file(filename);
    if (!file.is_open()) {
        return false;
    }
   std::ifstream file(filename);
   if (!file.is_open()) {
      return false;
   }
    std::unordered_map<std::string, RecipeInfo> recipeMap;
    std::string line;
    std::getline(file, line); // skip header
   std::unordered_map<std::string, RecipeInfo> recipeMap;
   std::string line;
   std::getline(file, line); // skip header
    while (std::getline(file, line)) {
        std::stringstream ss(line);
        std::string cell;
        std::string ppid, description, createTime;
        DeviceRecipe dev;
   while (std::getline(file, line)) {
      std::stringstream ss(line);
      std::string cell;
      std::string ppid, description, createTime;
      DeviceRecipe dev;
        std::getline(ss, ppid, ',');
        std::getline(ss, cell, ',');
        try { dev.nDeviceID = std::stoi(cell); }
        catch (...) { continue; }
        std::getline(ss, dev.strDeviceName, ',');
        std::getline(ss, cell, ',');
        try { dev.nRecipeID = std::stoi(cell); }
        catch (...) { continue; }
        std::getline(ss, description, ',');
        std::getline(ss, createTime, ',');
      std::getline(ss, ppid, ',');
      std::getline(ss, cell, ',');
      try { dev.nDeviceID = std::stoi(cell); }
      catch (...) { continue; }
      std::getline(ss, dev.strDeviceName, ',');
      std::getline(ss, cell, ',');
      try { dev.nRecipeID = std::stoi(cell); }
      catch (...) { continue; }
      std::getline(ss, dev.strRecipeName, ',');
      std::getline(ss, description, ',');
      std::getline(ss, createTime, ',');
        dev.strPPID = ppid;
        auto& recipe = recipeMap[ppid];
        recipe.strPPID = ppid;
        recipe.strDescription = description;
        recipe.strCreateTime = createTime;
        recipe.vecDeviceList.push_back(dev);
    }
      auto& recipe = recipeMap[ppid];
      recipe.strPPID = ppid;
      recipe.strDescription = description;
      recipe.strCreateTime = createTime;
      recipe.vecDeviceList.push_back(dev);
   }
    for (const auto& pair : recipeMap) {
        if (!updateRecipe(pair.second)) {
            std::cerr << "Failed to update recipe from file: " << pair.first << std::endl;
        }
    }
   for (const auto& pair : recipeMap) {
      if (!updateRecipe(pair.second)) {
         std::cerr << "Failed to update recipe from file: " << pair.first << std::endl;
      }
   }
    return true;
   return true;
}
bool RecipeManager::saveRecipeFile(const std::string& filename) {
    if (!m_pDB) {
        return false;
    }
   if (!m_pDB) {
      return false;
   }
    std::ofstream file(filename);
    if (!file.is_open()) {
        return false;
    }
   std::ofstream file(filename);
   if (!file.is_open()) {
      return false;
   }
    file << "PPID,DeviceID,DeviceName,RecipeID,Description,CreateTime\n";
    auto recipes = getAllRecipes();
    for (const auto& recipe : recipes) {
        for (const auto& dev : recipe.vecDeviceList) {
            file << recipe.strPPID << ","
                << dev.nDeviceID << ","
                << dev.strDeviceName << ","
                << dev.nRecipeID << ","
                << recipe.strDescription << ","
                << recipe.strCreateTime << "\n";
        }
    }
   file << "PPID,DeviceID,DeviceName,RecipeID,RecipeName,Description,CreateTime\n";
   auto recipes = getAllRecipes();
   for (const auto& recipe : recipes) {
      for (const auto& dev : recipe.vecDeviceList) {
         file << recipe.strPPID << ","
            << dev.nDeviceID << ","
            << dev.strDeviceName << ","
            << dev.nRecipeID << ","
            << dev.strRecipeName << ","
            << recipe.strDescription << ","
            << recipe.strCreateTime << "\n";
      }
   }
    return true;
   return true;
}