| | |
| | | #include "stdafx.h" |
| | | #include "stdafx.h" |
| | | #include "RecipeManager.h" |
| | | #include <sstream> |
| | | #include <iomanip> |
| | |
| | | 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::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; |
| | | } |
| | | |
| | | 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) { |
| | |
| | | } |
| | | |
| | | 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()); |
| | |
| | | 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" |
| | | << ");"; |
| | | m_pDB->executeQuery(sql.str()); |
| | | |
| | | 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, "标准工艺", ""); |
| | | addDeviceRecipe("Bonder1", 102, "改良工艺", ""); |
| | | addDeviceRecipe("Bonder1", 103, "高速模式", ""); |
| | | |
| | | addDeviceRecipe("Bonder2", 101, "标准工艺", ""); |
| | | addDeviceRecipe("Bonder2", 102, "改良工艺", ""); |
| | | addDeviceRecipe("Bonder2", 103, "高速模式", ""); |
| | | } |
| | | |
| | | 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; |
| | | } |