From 2ece96546ec80bf26013d005967cbfdc06156ee7 Mon Sep 17 00:00:00 2001
From: mrDarker <mr.darker@163.com>
Date: 星期二, 17 六月 2025 16:32:41 +0800
Subject: [PATCH] 1. 添加配方管理类(数据库) 2. 修改用户管理和系统日志管理类的路径和名称 3. 修复合并代码导致系统日志管理窗口的资源异常的问题
---
SourceCode/Bond/Servo/RecipeManager.cpp | 446 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 446 insertions(+), 0 deletions(-)
diff --git a/SourceCode/Bond/Servo/RecipeManager.cpp b/SourceCode/Bond/Servo/RecipeManager.cpp
new file mode 100644
index 0000000..247cd40
--- /dev/null
+++ b/SourceCode/Bond/Servo/RecipeManager.cpp
@@ -0,0 +1,446 @@
+#include "stdafx.h"
+#include "RecipeManager.h"
+#include <sstream>
+#include <iomanip>
+#include <fstream>
+#include <iostream>
+
+std::recursive_mutex RecipeManager::m_mutex;
+
+RecipeManager& RecipeManager::getInstance() {
+ static RecipeManager instance;
+ return instance;
+}
+
+RecipeManager::RecipeManager() {
+ m_pDB = new BL::SQLiteDatabase();
+}
+
+RecipeManager::~RecipeManager() {
+ 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);
+
+ 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;
+ }
+
+ 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 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,
+ UNIQUE (ppid, device_id),
+ UNIQUE (ppid, device_name)
+ );
+ )";
+
+ return m_pDB->executeQuery(createRecipeTable)
+ && m_pDB->executeQuery(createDeviceTable);
+}
+
+void RecipeManager::termRecipeTable() {
+ if (!m_pDB) {
+ return;
+ }
+
+ m_pDB->disconnect();
+}
+
+bool RecipeManager::destroyRecipeTable() {
+ if (!m_pDB) {
+ return false;
+ }
+
+ 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");
+}
+
+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");
+}
+
+bool RecipeManager::addRecipe(const RecipeInfo& recipe) {
+ 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::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ // 开始事务
+ m_pDB->executeQuery("BEGIN TRANSACTION;");
+
+ 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;
+ }
+
+ 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 << ");";
+
+ 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;
+ }
+
+ // 检查 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::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;
+ }
+
+ 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());
+}
+
+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;
+ }
+
+ 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::vector<RecipeInfo> RecipeManager::getAllRecipes() {
+ if (!m_pDB) {
+ return {};
+ }
+
+ 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];
+
+ 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());
+
+ 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);
+ }
+
+ return recipes;
+}
+
+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;
+ }
+
+ 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;
+}
+
+int RecipeManager::getDeviceRecipeIDByID(const std::string& ppid, int nDeviceID) {
+ 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 << ";";
+
+ 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;
+ }
+
+ 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;
+}
+
+bool RecipeManager::deleteRecipeByPPID(const std::string& ppid) {
+ if (!m_pDB) {
+ return false;
+ }
+
+ 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 (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);
+}
+
+bool RecipeManager::updateDeviceRecipeIDByID(const std::string& ppid, int nDeviceID, int nNewRecipeID) {
+ if (!m_pDB || ppid.empty() || nDeviceID <= 0 || nNewRecipeID <= 0) {
+ return false;
+ }
+
+ std::ostringstream query;
+ 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());
+}
+
+bool RecipeManager::updateDeviceRecipeIDByName(const std::string& ppid, const std::string& strDeviceName, int nNewRecipeID) {
+ if (!m_pDB || ppid.empty() || strDeviceName.empty() || nNewRecipeID <= 0) {
+ return false;
+ }
+ std::ostringstream query;
+ 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());
+}
+
+void RecipeManager::insertMockData() {
+ if (!m_pDB) {
+ return;
+ }
+
+ RecipeInfo recipe;
+ recipe.strPPID = "P1001";
+ recipe.strDescription = "Main Board Burn-in";
+
+ recipe.vecDeviceList = {
+ {1, 101, "P1001","Burner A"},
+ {2, 102, "P1001", "Burner B"}
+ };
+
+ addRecipe(recipe);
+}
+
+bool RecipeManager::readRecipeFile(const std::string& filename) {
+ if (!m_pDB) {
+ 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
+
+ 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, ',');
+
+ dev.strPPID = ppid;
+ 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;
+ }
+ }
+
+ return true;
+}
+
+bool RecipeManager::saveRecipeFile(const std::string& filename) {
+ if (!m_pDB) {
+ 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";
+ }
+ }
+
+ return true;
+}
\ No newline at end of file
--
Gitblit v1.9.3