#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 (
|
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,
|
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);
|
}
|
|
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, 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;
|
}
|
|
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, 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());
|
}
|
|
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, 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;
|
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;
|
}
|
|
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;
|
|
if (!m_pDB) {
|
return vecPPID;
|
}
|
|
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]);
|
}
|
}
|
|
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;
|
}
|
|
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, 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;
|
}
|
|
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::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;
|
}
|
|
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;
|
}
|
|
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;
|
}
|
|
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;
|
}
|
|
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());
|
}
|
|
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());
|
}
|
|
bool RecipeManager::addDeviceRecipe(const std::string& deviceName, int nRecipeID, const std::string& strRecipeName) {
|
if (!m_pDB || deviceName.empty() || nRecipeID <= 0 || strRecipeName.empty()) {
|
return false;
|
}
|
|
std::ostringstream sql;
|
sql << "CREATE TABLE IF NOT EXISTS " << deviceName << "_Recipes ("
|
<< "recipe_id INTEGER PRIMARY KEY, "
|
<< "recipe_name TEXT NOT NULL"
|
<< ");";
|
m_pDB->executeQuery(sql.str());
|
|
std::ostringstream ins;
|
ins << "INSERT OR REPLACE INTO " << deviceName << "_Recipes (recipe_id, recipe_name) VALUES ("
|
<< nRecipeID << ", '" << strRecipeName << "');";
|
|
std::lock_guard<std::recursive_mutex> lk(m_mutex);
|
return m_pDB->executeQuery(ins.str());
|
}
|
|
bool RecipeManager::updateDeviceRecipe(const std::string& deviceName, int nRecipeID, const std::string& newName) {
|
if (!m_pDB || deviceName.empty() || nRecipeID <= 0 || newName.empty()) {
|
return false;
|
}
|
|
std::ostringstream sql;
|
sql << "UPDATE " << deviceName << "_Recipes SET recipe_name='" << newName
|
<< "' WHERE recipe_id=" << nRecipeID << ";";
|
|
std::lock_guard<std::recursive_mutex> lk(m_mutex);
|
return m_pDB->executeQuery(sql.str());
|
}
|
|
std::string RecipeManager::getDeviceRecipeName(const std::string& deviceName, int nRecipeID) {
|
if (!m_pDB || deviceName.empty() || nRecipeID <= 0) {
|
return "";
|
}
|
|
std::ostringstream sql;
|
sql << "SELECT recipe_name FROM " << deviceName << "_Recipes "
|
<< "WHERE recipe_id=" << nRecipeID << " 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& deviceName, int nRecipeID) {
|
if (!m_pDB || deviceName.empty() || nRecipeID <= 0) {
|
return false;
|
}
|
|
std::ostringstream sql;
|
sql << "DELETE FROM " << deviceName << "_Recipes WHERE recipe_id=" << nRecipeID << ";";
|
|
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& deviceName) {
|
std::vector<std::pair<int, std::string>> out;
|
if (!m_pDB || deviceName.empty()) {
|
return out;
|
}
|
|
std::ostringstream sql;
|
sql << "SELECT recipe_id, recipe_name FROM " << deviceName << "_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;
|
}
|
|
RecipeInfo recipe;
|
recipe.strPPID = "P1001";
|
recipe.strDescription = "Main Board Burn-in";
|
|
recipe.vecDeviceList = {
|
{9, 101, "VacuumBake", "VacuumBake"},
|
{10, 102, "Bonder1", "Bonder1"},
|
{11, 103, "Bonder2", "Bonder2"}
|
};
|
|
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;
|
}
|
|
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, dev.strRecipeName, ',');
|
std::getline(ss, description, ',');
|
std::getline(ss, createTime, ',');
|
|
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,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;
|
}
|