From 958cfa6b2e1c1eec0e20edc50816ef18f10cbf09 Mon Sep 17 00:00:00 2001
From: mrDarker <mr.darker@163.com>
Date: 星期四, 29 五月 2025 15:50:34 +0800
Subject: [PATCH] 1. 修改本地模式Port设置界面 2. 添加Transfer数据库管理类

---
 SourceCode/Bond/Servo/Servo.vcxproj            |    2 
 SourceCode/Bond/Servo/PortConfigurationDlg.cpp |   28 +-
 SourceCode/Bond/Servo/Servo.vcxproj.filters    |    2 
 SourceCode/Bond/Servo/resource.h               |    0 
 SourceCode/Bond/Servo/TransferManager.h        |  171 ++++++++++++
 SourceCode/Bond/Servo/Servo.cpp                |   38 +-
 SourceCode/Bond/Servo/Servo.rc                 |    0 
 SourceCode/Bond/Servo/TransferManager.cpp      |  550 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 757 insertions(+), 34 deletions(-)

diff --git a/SourceCode/Bond/Servo/PortConfigurationDlg.cpp b/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
index b19be21..79a1e11 100644
--- a/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
+++ b/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
@@ -30,7 +30,7 @@
         return;
     }
 
-    const int nCols = 4;
+    const int nCols = 3;
     const int nFixRows = 1;
     const int nRows = 9;
 
@@ -87,10 +87,10 @@
 
 void CPortConfigurationDlg::FillGrid()
 {
-    CStringArray recipeOptions;
-    recipeOptions.Add(_T("Recipe A"));
-    recipeOptions.Add(_T("Recipe B"));
-    recipeOptions.Add(_T("Recipe C"));
+    //CStringArray recipeOptions;
+    //recipeOptions.Add(_T("Recipe A"));
+    //recipeOptions.Add(_T("Recipe B"));
+    //recipeOptions.Add(_T("Recipe C"));
 
     for (int i = 1; i < 9; ++i) {
         CString strIndex;
@@ -99,19 +99,19 @@
         m_wndGrid.SetItemState(i, 0, GVIS_READONLY);
 
         // EQ Recipe - ComboBox
-        if (m_wndGrid.SetCellType(i, 1, RUNTIME_CLASS(CGridCellCombo))) {
-            CGridCellCombo* pCell = static_cast<CGridCellCombo*>(m_wndGrid.GetCell(i, 1));
-            pCell->SetOptions(recipeOptions);
-            pCell->SetStyle(CBS_DROPDOWNLIST);
-        }
-        m_wndGrid.SetItemText(i, 1, recipeOptions[0]);
+        //if (m_wndGrid.SetCellType(i, 1, RUNTIME_CLASS(CGridCellCombo))) {
+        //    CGridCellCombo* pCell = static_cast<CGridCellCombo*>(m_wndGrid.GetCell(i, 1));
+        //    pCell->SetOptions(recipeOptions);
+        //    pCell->SetStyle(CBS_DROPDOWNLIST);
+        //}
+        //m_wndGrid.SetItemText(i, 1, recipeOptions[0]);
 
         // Panel ID - 鍙紪杈�
-        m_wndGrid.SetItemText(i, 2, _T(""));
+        m_wndGrid.SetItemText(i, 1, _T(""));
 
         // Checkbox
-        m_wndGrid.SetCellType(i, 3, RUNTIME_CLASS(CGridCellCheck));
-        CGridCellCheck* pCheck = static_cast<CGridCellCheck*>(m_wndGrid.GetCell(i, 3));
+        m_wndGrid.SetCellType(i, 2, RUNTIME_CLASS(CGridCellCheck));
+        CGridCellCheck* pCheck = static_cast<CGridCellCheck*>(m_wndGrid.GetCell(i, 2));
         if (pCheck) {
             pCheck->SetCheck(FALSE);
         }
diff --git a/SourceCode/Bond/Servo/Servo.cpp b/SourceCode/Bond/Servo/Servo.cpp
index 3b5e1c5..18a95a1 100644
--- a/SourceCode/Bond/Servo/Servo.cpp
+++ b/SourceCode/Bond/Servo/Servo.cpp
@@ -8,7 +8,7 @@
 #include "ServoGraph.h"
 #include "AlarmManager.h"
 #include "SECSRuntimeManager.h"
-#include "ProductionLogManager.h"
+#include "TransferManager.h"
 #include "VerticalLine.h"
 #include "EqsGraphWnd.h"
 #include "MapPosWnd.h"
@@ -132,22 +132,6 @@
 	}
 	AlarmManager::getInstance().insertMockData();
 
-
-	// 初始化生产履历管理器
-	//try {
-	//	if (!ProductionLogManager::getInstance().initProductionTable()) {
-	//		AfxMessageBox("初始化生产履历管理器失败!");
-	//		return FALSE;
-	//	}
-	//}
-	//catch (const std::exception& ex) {
-	//	CString errorMsg;
-	//	errorMsg.Format(_T("初始化生产履历管理器失败:%s"), CString(ex.what()));
-	//	AfxMessageBox(errorMsg, MB_ICONERROR);
-	//	return FALSE;
-	//}
-
-
 	// 初始化SECS运行设置管理库
 	try {
 		if (!SECSRuntimeManager::getInstance().initRuntimeSetting()) {
@@ -158,6 +142,20 @@
 	catch (const std::exception& ex) {
 		CString errorMsg;
 		errorMsg.Format(_T("初始化SECS运行设置失败:%s"), CString(ex.what()));
+		AfxMessageBox(errorMsg, MB_ICONERROR);
+		return FALSE;
+	}
+
+	// 初始化搬运记录管理库
+	try {
+		if (!TransferManager::getInstance().initTransferTable()) {
+			AfxMessageBox("初始化搬运记录管理库设置失败!");
+			return FALSE;
+		}
+	}
+	catch (const std::exception& ex) {
+		CString errorMsg;
+		errorMsg.Format(_T("初始化搬运记录管理库设置失败:%s"), CString(ex.what()));
 		AfxMessageBox(errorMsg, MB_ICONERROR);
 		return FALSE;
 	}
@@ -205,12 +203,12 @@
 	// 销毁报警表
 	AlarmManager::getInstance().termAlarmTable();
 
-	// 销毁生产表
-	ProductionLogManager::getInstance().termProductionTable();
-
 	// 销毁SECS运行设置管理库
 	SECSRuntimeManager::getInstance().termRuntimeSetting();
 
+	// 销毁搬运记录管理库
+	TransferManager::getInstance().termTransferTable();
+
 	return CWinApp::ExitInstance();
 }
 
diff --git a/SourceCode/Bond/Servo/Servo.rc b/SourceCode/Bond/Servo/Servo.rc
index d392894..140d51f 100644
--- a/SourceCode/Bond/Servo/Servo.rc
+++ b/SourceCode/Bond/Servo/Servo.rc
Binary files differ
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj b/SourceCode/Bond/Servo/Servo.vcxproj
index 21de14d..11eb375 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj
+++ b/SourceCode/Bond/Servo/Servo.vcxproj
@@ -317,6 +317,7 @@
     <ClInclude Include="targetver.h" />
     <ClInclude Include="TerminalDisplayDlg.h" />
     <ClInclude Include="ToolUnits.h" />
+    <ClInclude Include="TransferManager.h" />
     <ClInclude Include="VerticalLine.h" />
   </ItemGroup>
   <ItemGroup>
@@ -438,6 +439,7 @@
     </ClCompile>
     <ClCompile Include="TerminalDisplayDlg.cpp" />
     <ClCompile Include="ToolUnits.cpp" />
+    <ClCompile Include="TransferManager.cpp" />
     <ClCompile Include="VerticalLine.cpp" />
   </ItemGroup>
   <ItemGroup>
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj.filters b/SourceCode/Bond/Servo/Servo.vcxproj.filters
index b2ae64c..1a8bb3f 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj.filters
+++ b/SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -149,6 +149,7 @@
       <Filter>GridControl\NewCellTypes</Filter>
     </ClCompile>
     <ClCompile Include="PortConfigurationDlg.cpp" />
+    <ClCompile Include="TransferManager.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="AlarmManager.h" />
@@ -303,6 +304,7 @@
       <Filter>GridControl\NewCellTypes</Filter>
     </ClInclude>
     <ClInclude Include="PortConfigurationDlg.h" />
+    <ClInclude Include="TransferManager.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Servo.rc" />
diff --git a/SourceCode/Bond/Servo/TransferManager.cpp b/SourceCode/Bond/Servo/TransferManager.cpp
new file mode 100644
index 0000000..bfcf457
--- /dev/null
+++ b/SourceCode/Bond/Servo/TransferManager.cpp
@@ -0,0 +1,550 @@
+#include "stdafx.h"
+#include "TransferManager.h"
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <iomanip>
+#include <ctime>
+#include <stdexcept>
+
+// 静态互斥锁初始化
+std::mutex TransferManager::m_mutex;
+
+// 获取 TransferManager 单例
+TransferManager& TransferManager::getInstance() {
+    static TransferManager instance;
+    return instance;
+}
+
+// 构造函数
+TransferManager::TransferManager() {
+    m_pDB = new BL::SQLiteDatabase();
+}
+
+// 析构函数
+TransferManager::~TransferManager() {
+    if (m_pDB) {
+        delete m_pDB;
+        m_pDB = nullptr;
+    }
+}
+
+// 任务状态转换成 int 类型
+int TransferManager::statusToInt(TransferStatus status) {
+    return static_cast<int>(status);
+}
+
+// int 类型转换成任务状态
+TransferStatus TransferManager::intToStatus(int value) {
+    switch (value) {
+        case 0: return TransferStatus::Ready;
+        case 1: return TransferStatus::Running;
+        case 2: return TransferStatus::Error;
+        case 3: return TransferStatus::Abort;
+        case 4: return TransferStatus::Completed;
+        default: return TransferStatus::Error;
+    }
+}
+
+// 任务状态转换成字符串
+std::string TransferManager::statusToString(TransferStatus status) {
+    switch (status) {
+        case TransferStatus::Ready:     return "Ready";
+        case TransferStatus::Running:   return "Running";
+        case TransferStatus::Error:     return "Error";
+        case TransferStatus::Abort:     return "Abort";
+        case TransferStatus::Completed: return "Completed";
+        default:                        return "Unknown";
+    }
+}
+
+// 字符串转换成任务状态
+TransferStatus TransferManager::stringToStatus(const std::string& str) {
+    if (str == "Ready")     return TransferStatus::Ready;
+    if (str == "Running")   return TransferStatus::Running;
+    if (str == "Error")     return TransferStatus::Error;
+    if (str == "Abort")     return TransferStatus::Abort;
+    if (str == "Completed") return TransferStatus::Completed;
+    return TransferStatus::Error;
+}
+
+// 本地编码转为 UTF-8
+std::string TransferManager::ansiToUtf8(const std::string& ansiStr) {
+    // 1. ANSI → UTF-16
+    int wideLen = MultiByteToWideChar(CP_ACP, 0, ansiStr.c_str(), -1, nullptr, 0);
+    std::wstring wideStr(wideLen, 0);
+    MultiByteToWideChar(CP_ACP, 0, ansiStr.c_str(), -1, &wideStr[0], wideLen);
+
+    // 2. UTF-16 → UTF-8
+    int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr);
+    std::string utf8Str(utf8Len, 0);
+    WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, &utf8Str[0], utf8Len, nullptr, nullptr);
+
+    utf8Str.pop_back(); // 去掉最后的 '\0'
+    return utf8Str;
+}
+
+// UTF-8 转为本地编码
+std::string TransferManager::utf8ToAnsi(const std::string& utf8Str) {
+    // 1. UTF-8 → UTF-16
+    int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, NULL, 0);
+    std::wstring wideStr(wideLen, 0);
+    MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, &wideStr[0], wideLen);
+
+    // 2. UTF-16 → ANSI
+    int ansiLen = WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, NULL, 0, NULL, NULL);
+    std::string ansiStr(ansiLen, 0);
+    WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, &ansiStr[0], ansiLen, NULL, NULL);
+
+    ansiStr.pop_back(); // 去掉最后的 '\0'
+    return ansiStr;
+}
+
+// 初始化搬运记录表
+bool TransferManager::initTransferTable() {
+    char szPath[MAX_PATH];
+    GetModuleFileName(NULL, szPath, MAX_PATH);
+    std::string exePath(szPath);
+    std::string dbDir = exePath.substr(0, exePath.find_last_of("\\/")) + "\\DB";
+
+    if (!CreateDirectory(dbDir.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
+        throw std::runtime_error("创建数据库目录失败");
+    }
+
+    std::string dbPath = dbDir + "\\TransferManager.db";
+
+    if (!m_pDB->connect(dbPath, true)) {
+        throw std::runtime_error("连接数据库失败");
+    }
+
+    const std::string createTableSQL = R"(
+        CREATE TABLE IF NOT EXISTS transfers (
+            record_id    INTEGER PRIMARY KEY AUTOINCREMENT,
+            class_id     TEXT NOT NULL,
+            status       TEXT NOT NULL,
+            create_time  TEXT,
+            pick_time    TEXT,
+            place_time   TEXT,
+            end_time     TEXT,
+            description  TEXT
+        );
+    )";
+
+    return m_pDB->executeQuery(createTableSQL);
+}
+
+// 插入测试搬运记录
+void TransferManager::insertTestTransferRecord() {
+    TransferData data;
+    data.strClassID = "Task-20240529-001";
+    data.strStatus = "Running";
+    data.strCreateTime = "2024-05-29 10:30:00";
+    data.strPickTime = "2024-05-29 10:31:00";
+    data.strPlaceTime = "2024-05-29 10:32:00";
+    data.strEndTime = "2024-05-29 10:33:00";
+    data.strDescription = "搬运动作:从 Port1 取片 → Port2 放片";
+
+    int nRecordId = -1;
+    if (TransferManager::getInstance().addTransferRecord(data, nRecordId)) {
+        std::cout << "插入成功,记录 ID = " << nRecordId << std::endl;
+    }
+    else {
+        std::cerr << "插入失败!" << std::endl;
+    }
+}
+
+// 断开数据库连接
+void TransferManager::termTransferTable() {
+    if (m_pDB) {
+        m_pDB->disconnect();
+    }
+}
+
+// 删除搬运记录表
+bool TransferManager::destroyTransferTable() {
+    if (!m_pDB) return false;
+
+    const std::string dropTableSQL = "DROP TABLE IF EXISTS transfers;";
+    return m_pDB->executeQuery(dropTableSQL);
+}
+
+// 插入搬运记录
+bool TransferManager::addTransferRecord(const TransferData& data, int& outRecordId) {
+    if (!m_pDB) {
+        return false;
+    }
+
+    std::ostringstream oss;
+    oss << "INSERT INTO transfers (class_id, status, create_time, pick_time, place_time, end_time, description) "
+        << "VALUES ('"
+        << ansiToUtf8(data.strClassID) << "', '"
+        << ansiToUtf8(data.strStatus) << "', '"
+        << data.strCreateTime << "', '"
+        << data.strPickTime << "', '"
+        << data.strPlaceTime << "', '"
+        << data.strEndTime << "', '"
+        << ansiToUtf8(data.strDescription) << "') RETURNING record_id;";
+
+    std::lock_guard<std::mutex> lock(m_mutex);
+
+    auto results = m_pDB->fetchResults(oss.str());
+    if (!results.empty() && !results[0].empty()) {
+        try {
+            outRecordId = std::stoi(results[0][0]);
+            return true;
+        }
+        catch (const std::exception& e) {
+            std::cerr << "解析 record_id 出错: " << e.what() << std::endl;
+        }
+    }
+
+    return false;
+}
+
+// 查询所有搬运记录
+std::vector<TransferData> TransferManager::getAllTransfers() {
+    std::vector<TransferData> records;
+
+    if (!m_pDB) {
+        return records;
+    }
+
+    const std::string query = R"(
+        SELECT record_id, class_id, status, create_time, pick_time, place_time, end_time, description
+        FROM transfers
+        ORDER BY record_id DESC
+    )";
+
+    auto results = m_pDB->fetchResults(query);
+    for (const auto& row : results) {
+        if (row.size() != 8) continue;
+
+        TransferData data;
+        data.nRecordId = std::stoi(row[0]);
+        data.strClassID = row[1];
+        data.strStatus = row[2];
+        data.strCreateTime = row[3];
+        data.strPickTime = row[4];
+        data.strPlaceTime = row[5];
+        data.strEndTime = row[6];
+        data.strDescription = row[7];
+
+        records.push_back(data);
+    }
+
+    return records;
+}
+
+// 根据 ID 查询搬运记录
+TransferData TransferManager::getTransferById(int id) {
+	TransferData data;
+	if (!m_pDB) {
+		return data;
+	}
+	std::ostringstream oss;
+	oss << "SELECT record_id, class_id, status, create_time, pick_time, place_time, end_time, description "
+		<< "FROM transfers WHERE record_id = " << id;
+	auto results = m_pDB->fetchResults(oss.str());
+	if (!results.empty() && results[0].size() == 8) {
+		data.nRecordId = std::stoi(results[0][0]);
+		data.strClassID = results[0][1];
+		data.strStatus = results[0][2];
+		data.strCreateTime = results[0][3];
+		data.strPickTime = results[0][4];
+		data.strPlaceTime = results[0][5];
+		data.strEndTime = results[0][6];
+		data.strDescription = results[0][7];
+	}
+	return data;
+}
+
+// 根据时间范围查询搬运记录
+std::vector<TransferData> TransferManager::getTransfersByTimeRange(const std::string& startTime, const std::string& endTime) {
+	std::vector<TransferData> records;
+	if (!m_pDB) {
+		return records;
+	}
+	std::ostringstream oss;
+	oss << "SELECT record_id, class_id, status, create_time, pick_time, place_time, end_time, description "
+		<< "FROM transfers WHERE 1=1";
+	if (!startTime.empty()) {
+		oss << " AND create_time >= '" << startTime << "'";
+	}
+	if (!endTime.empty()) {
+		oss << " AND end_time <= '" << endTime << "'";
+	}
+	auto results = m_pDB->fetchResults(oss.str());
+	for (const auto& row : results) {
+		if (row.size() != 8) continue;
+		TransferData data;
+		data.nRecordId = std::stoi(row[0]);
+		data.strClassID = row[1];
+		data.strStatus = row[2];
+		data.strCreateTime = row[3];
+		data.strPickTime = row[4];
+		data.strPlaceTime = row[5];
+		data.strEndTime = row[6];
+		data.strDescription = row[7];
+		records.push_back(data);
+	}
+	return records;
+}
+
+// 查询指定状态的搬运记录
+std::vector<TransferData> TransferManager::getTransfersByStatus(const std::string& status) {
+    std::vector<TransferData> records;
+    if (!m_pDB) {
+        return records;
+    }
+
+    std::ostringstream oss;
+    oss << "SELECT record_id, class_id, status, create_time, pick_time, place_time, end_time, description "
+        << "FROM transfers WHERE status = '" << status << "' "
+        << "ORDER BY create_time DESC";
+
+    auto results = m_pDB->fetchResults(oss.str());
+    for (const auto& row : results) {
+        if (row.size() != 8) continue;
+
+        TransferData data;
+        data.nRecordId = std::stoi(row[0]);
+        data.strClassID = row[1];
+        data.strStatus = row[2];
+        data.strCreateTime = row[3];
+        data.strPickTime = row[4];
+        data.strPlaceTime = row[5];
+        data.strEndTime = row[6];
+        data.strDescription = row[7];
+
+        records.push_back(data);
+    }
+
+    return records;
+}
+
+// 分页获取搬运记录
+//std::vector<TransferData> TransferManager::getTransfers(int startPosition, int count) {
+//	std::vector<TransferData> records;
+//	if (!m_pDB) {
+//		return records;
+//	}
+//	std::ostringstream oss;
+//	oss << "SELECT record_id, class_id, status, create_time, pick_time, place_time, end_time, description "
+//		<< "FROM transfers ORDER BY create_time DESC LIMIT " << count << " OFFSET " << startPosition;
+//	auto results = m_pDB->fetchResults(oss.str());
+//	for (const auto& row : results) {
+//		if (row.size() != 8) continue;
+//		TransferData data;
+//		data.nRecordId = std::stoi(row[0]);
+//		data.strClassID = row[1];
+//		data.strStatus = row[2];
+//		data.strCreateTime = row[3];
+//		data.strPickTime = row[4];
+//		data.strPlaceTime = row[5];
+//		data.strEndTime = row[6];
+//		data.strDescription = row[7];
+//		records.push_back(data);
+//	}
+//	return records;
+//}
+
+// 获取符合条件的记录总数
+int TransferManager::getTotalTransferCount() {
+    if (!m_pDB) {
+        return 0;
+    }
+
+    const std::string query = "SELECT COUNT(*) FROM transfers;";
+    auto results = m_pDB->fetchResults(query);
+
+    if (!results.empty() && !results[0].empty()) {
+        try {
+            return std::stoi(results[0][0]);
+        }
+        catch (const std::exception& e) {
+            std::cerr << "Error parsing total count: " << e.what() << std::endl;
+        }
+    }
+
+    return 0;
+}
+
+// 获取符合条件的记录总数
+int TransferManager::getTotalTransferCount(const TransferData& filter) {
+    if (!m_pDB) {
+        return 0;
+    }
+
+    std::ostringstream oss;
+    oss << "SELECT COUNT(*) FROM transfers WHERE 1=1";
+
+    // 状态筛选(完全匹配)
+    if (!filter.strStatus.empty()) {
+        oss << " AND status = '" << filter.strStatus << "'";
+    }
+
+    // 描述关键字模糊匹配
+    if (!filter.strDescription.empty()) {
+        oss << " AND description LIKE '%" << filter.strDescription << "%'";
+    }
+
+    // 时间范围筛选
+    if (!filter.strCreateTime.empty()) {
+        oss << " AND create_time >= '" << filter.strCreateTime << "'";
+    }
+    if (!filter.strEndTime.empty()) {
+        oss << " AND end_time <= '" << filter.strEndTime << "'";
+    }
+
+    auto results = m_pDB->fetchResults(oss.str());
+    if (!results.empty() && !results[0].empty()) {
+        try {
+            return std::stoi(results[0][0]);
+        }
+        catch (const std::exception& e) {
+            std::cerr << "Error parsing total count: " << e.what() << std::endl;
+        }
+    }
+
+    return 0;
+}
+
+std::vector<TransferData> TransferManager::getTransfers(const TransferData& filter, int pageNum, int pageSize) {
+    std::vector<TransferData> records;
+
+    if (!m_pDB) {
+        return records;
+    }
+
+    std::ostringstream oss;
+    oss << "SELECT record_id, class_id, status, create_time, pick_time, place_time, end_time, description "
+        << "FROM transfers WHERE 1=1";
+
+    // 条件拼接(与 getTotalTransferCount 保持一致)
+    if (!filter.strStatus.empty()) {
+        oss << " AND status = '" << filter.strStatus << "'";
+    }
+    if (!filter.strDescription.empty()) {
+        oss << " AND description LIKE '%" << filter.strDescription << "%'";
+    }
+    if (!filter.strCreateTime.empty()) {
+        oss << " AND create_time >= '" << filter.strCreateTime << "'";
+    }
+    if (!filter.strEndTime.empty()) {
+        oss << " AND end_time <= '" << filter.strEndTime << "'";
+    }
+
+    // 分页控制
+    int offset = (pageNum - 1) * pageSize;
+    oss << " ORDER BY create_time DESC";
+    oss << " LIMIT " << pageSize << " OFFSET " << offset;
+
+    // 查询
+    auto results = m_pDB->fetchResults(oss.str());
+    for (const auto& row : results) {
+        if (row.size() != 8) continue;
+
+        TransferData data;
+        data.nRecordId = std::stoi(row[0]);
+        data.strClassID = row[1];
+        data.strStatus = row[2];
+        data.strCreateTime = row[3];
+        data.strPickTime = row[4];
+        data.strPlaceTime = row[5];
+        data.strEndTime = row[6];
+        data.strDescription = row[7];
+
+        records.push_back(data);
+    }
+
+    return records;
+}
+
+// 清理早于某一时间的搬运记录
+void TransferManager::cleanOldTransfers(int daysToKeep) {
+	if (!m_pDB) {
+		return;
+	}
+	std::ostringstream oss;
+	oss << "DELETE FROM transfers WHERE create_time < datetime('now', '-" << daysToKeep << " days')";
+
+	m_pDB->executeQuery(oss.str());
+}
+
+// 读取搬运记录 CSV 文件
+bool TransferManager::readTransferFile(const std::string& filename) {
+    std::ifstream file(filename);
+    if (!file.is_open()) {
+        std::cerr << "无法打开文件: " << filename << std::endl;
+        return false;
+    }
+
+    std::string line;
+    bool firstLine = true;
+    int insertedCount = 0;
+
+    while (std::getline(file, line)) {
+        if (firstLine) {
+            firstLine = false;
+            continue;
+        }
+
+        std::stringstream ss(line);
+        std::string cell;
+        TransferData data;
+
+        try {
+            std::getline(ss, cell, ',');
+            data.nRecordId = std::stoi(cell);
+
+            std::getline(ss, data.strClassID, ',');
+            std::getline(ss, data.strStatus, ',');
+            std::getline(ss, data.strCreateTime, ',');
+            std::getline(ss, data.strPickTime, ',');
+            std::getline(ss, data.strPlaceTime, ',');
+            std::getline(ss, data.strEndTime, ',');
+            std::getline(ss, data.strDescription, ',');
+
+            int newId = -1;
+            if (addTransferRecord(data, newId)) {
+                ++insertedCount;
+            }
+        }
+        catch (const std::exception& e) {
+            std::cerr << "读取错误行: " << line << ",错误: " << e.what() << std::endl;
+            continue;
+        }
+    }
+
+    file.close();
+    std::cout << "成功导入记录数: " << insertedCount << std::endl;
+    return true;
+}
+
+// 保存搬运记录到 CSV 文件
+bool TransferManager::saveTransferFile(const std::string& filename) {
+    std::ofstream file(filename);
+    if (!file.is_open()) {
+        std::cerr << "无法写入文件: " << filename << std::endl;
+        return false;
+    }
+
+    // 写入标题
+    file << "RecordID,ClassID,Status,CreateTime,PickTime,PlaceTime,EndTime,Description\n";
+
+    auto records = getAllTransfers();
+    for (const auto& data : records) {
+        file << data.nRecordId << ","
+            << data.strClassID << ","
+            << data.strStatus << ","
+            << data.strCreateTime << ","
+            << data.strPickTime << ","
+            << data.strPlaceTime << ","
+            << data.strEndTime << ","
+            << data.strDescription << "\n";
+    }
+
+    file.close();
+    std::cout << "已导出 " << records.size() << " 条记录到: " << filename << std::endl;
+    return true;
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/TransferManager.h b/SourceCode/Bond/Servo/TransferManager.h
new file mode 100644
index 0000000..ed1bbf6
--- /dev/null
+++ b/SourceCode/Bond/Servo/TransferManager.h
@@ -0,0 +1,171 @@
+#ifndef TRANSFER_MANAGER_H
+#define TRANSFER_MANAGER_H
+
+#include <string>
+#include <vector>
+#include <mutex>
+#include <unordered_map>
+#include "Database.h"
+
+/**
+ * 任务状态枚举类型
+ */
+enum class TransferStatus {
+    Ready = 0,
+    Running,
+    Error,
+    Abort,
+    Completed
+};
+
+/**
+ * 搬运记录结构体
+ */
+struct TransferData {
+    int nRecordId;                  // 主键
+    std::string strClassID;         // 搬运任务 ClassID
+    std::string strStatus;          // 任务状态(字符串:Ready、Running...)
+    std::string strCreateTime;      // 创建时间
+    std::string strPickTime;        // 取片时间
+    std::string strPlaceTime;       // 放片时间
+    std::string strEndTime;         // 结束时间
+    std::string strDescription;     // 任务说明
+};
+
+using TransferDataMap = std::unordered_map<int, TransferData>;
+
+class TransferManager {
+public:
+    /**
+     * 获取 TransferManager 单例
+     * @return TransferManager 实例
+     */
+    static TransferManager& getInstance();
+
+    /**
+     * 初始化搬运记录表
+     * @return 成功返回 true,失败返回 false
+     */
+    bool initTransferTable();
+
+	/**
+	 * 插入测试搬运记录
+	 */
+    void insertTestTransferRecord();
+
+    /**
+     * 终止数据库连接
+     */
+    void termTransferTable();
+
+    /**
+     * 删除搬运记录表
+     * @return 成功返回 true,失败返回 false
+     */
+    bool destroyTransferTable();
+
+    /**
+     * 插入搬运记录
+     * @param data 搬运记录结构体
+     * @param outRecordId 插入后的记录主键 ID
+     * @return 成功返回 true,失败返回 false
+     */
+    bool addTransferRecord(const TransferData& data, int& outRecordId);
+
+    /**
+     * 查询所有搬运记录
+     * @return 所有记录数据
+     */
+    std::vector<TransferData> getAllTransfers();
+
+	/**
+	 * 根据记录 ID 查询搬运记录
+	 * @param id 记录主键 ID
+	 * @return 搬运记录数据
+	 */
+    TransferData getTransferById(int id);
+
+    /**
+     * 根据时间范围查询搬运记录
+     * @param startTime 起始时间
+     * @param endTime 结束时间
+     * @return 搬运记录数据
+     */
+    std::vector<TransferData> getTransfersByTimeRange(const std::string& startTime, const std::string& endTime);
+
+	/**
+	 * 根据状态查询搬运记录
+	 * @param status 任务状态
+	 * @return 搬运记录数据
+	 */
+    std::vector<TransferData> TransferManager::getTransfersByStatus(const std::string& status);
+
+    /**
+     * 分页获取搬运记录
+     * @param startPosition 起始记录位置
+     * @param count 获取的记录数量
+     * @return 搬运记录数据
+     */
+    //std::vector<TransferData> getTransfers(int startPosition, int count);
+
+    /**
+	 * 获取符合条件的记录总数
+     */
+    int getTotalTransferCount();
+
+    /**
+	 * 分页获取符合条件的搬运记录
+	 * @param filter 过滤条件
+	 * @param pageNum 页码
+	 * @param pageSize 每页记录数
+     */
+    std::vector<TransferData> getTransfers(const TransferData& filter, int pageNum, int pageSize);
+
+	/**
+	 * 获取符合条件的记录总数
+	 * @param filter 过滤条件
+	 * @return 符合条件的记录总数
+	 */
+    int getTotalTransferCount(const TransferData& filter);;
+
+    /**
+     * 清理早于某一时间的搬运记录
+     * @param daysToKeep 保留的天数
+     */
+    void cleanOldTransfers(int daysToKeep = 30);
+
+    /**
+     * 读取搬运记录 CSV 文件
+     * @param filename 文件名
+     * @return 成功返回 true,失败返回 false
+     */
+    bool readTransferFile(const std::string& filename);
+
+    /**
+     * 保存搬运记录到 CSV 文件
+     * @param filename 文件名
+     * @return 成功返回 true,失败返回 false
+     */
+    bool saveTransferFile(const std::string& filename);
+
+private:
+    TransferManager();
+    ~TransferManager();
+
+    // 禁止拷贝和赋值
+    TransferManager(const TransferManager&) = delete;
+    TransferManager& operator=(const TransferManager&) = delete;
+
+    // 内联函数
+    inline int statusToInt(TransferStatus status);
+    inline TransferStatus intToStatus(int value);
+    inline std::string statusToString(TransferStatus status);
+    inline TransferStatus stringToStatus(const std::string& str);
+    inline std::string ansiToUtf8(const std::string& ansiStr);
+    inline std::string utf8ToAnsi(const std::string& utf8Str);
+
+    BL::Database* m_pDB;
+    static std::mutex m_mutex;
+};
+
+#endif // TRANSFER_MANAGER_H
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/resource.h b/SourceCode/Bond/Servo/resource.h
index a205193..f530d52 100644
--- a/SourceCode/Bond/Servo/resource.h
+++ b/SourceCode/Bond/Servo/resource.h
Binary files differ

--
Gitblit v1.9.3