From 0569c29b19e4d23f055845a167c706f11590fa2a Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期二, 19 八月 2025 15:55:00 +0800
Subject: [PATCH] 1.CControlJob和CProcessJob的序列化和反序列化;

---
 SourceCode/Bond/Servo/Servo.vcxproj         |    1 
 SourceCode/Bond/Servo/ProcessJob.h          |   10 +
 SourceCode/Bond/Servo/Servo.vcxproj.filters |    1 
 SourceCode/Bond/Servo/ProcessJob.cpp        |  124 +++++++++++++++++
 SourceCode/Bond/Servo/CControlJob.cpp       |   80 +++++++++++
 SourceCode/Bond/Servo/CControlJob.h         |    9 +
 SourceCode/Bond/Servo/CMaster.cpp           |   85 ++++++++++++
 SourceCode/Bond/Servo/SerializeUtil.h       |   72 ++++++++++
 SourceCode/Bond/Servo/CMaster.h             |    3 
 SourceCode/Bond/Servo/Model.cpp             |    8 +
 10 files changed, 392 insertions(+), 1 deletions(-)

diff --git a/SourceCode/Bond/Servo/CControlJob.cpp b/SourceCode/Bond/Servo/CControlJob.cpp
index 3f0237b..e88f816 100644
--- a/SourceCode/Bond/Servo/CControlJob.cpp
+++ b/SourceCode/Bond/Servo/CControlJob.cpp
@@ -1,6 +1,7 @@
 #include "stdafx.h"
 #include "CControlJob.h"
 #include <cctype>
+#include "SerializeUtil.h"
 
 static inline std::string trimCopy(std::string s) {
     auto notspace = [](int ch) { return !std::isspace(ch); };
@@ -10,6 +11,11 @@
 }
 
 namespace SERVO {
+    CControlJob::CControlJob()
+    {
+
+    }
+
     CControlJob::CControlJob(std::string cjId)
         : m_cjId(trimCopy(std::move(cjId)))
     {
@@ -221,4 +227,78 @@
             return c >= 0x20 && c <= 0x7E;
             });
     }
+
+    void CControlJob::serialize(std::ostream& os) const {
+        write_pod(os, CJ_MAGIC);
+        write_pod(os, CJ_VERSION);
+
+        // 标识/优先级/状态/失败原因
+        write_string(os, id());                             // 或 m_cjId
+        write_pod<uint8_t>(os, priority());                 // 或 m_priority
+        write_pod<uint8_t>(os, static_cast<uint8_t>(state())); // 或 m_state
+        write_string(os, failReason());                     // 或 m_failReason
+
+        // 时间戳
+        write_opt_time(os, tQueued());
+        write_opt_time(os, tStart());
+        write_opt_time(os, tEnd());
+
+        // 关联 PJ 列表
+        write_vec_str(os, pjIds());                         // 或 m_pjIds
+    }
+
+    bool CControlJob::deserialize(std::istream& is, CControlJob& out, std::string* err) {
+        auto fail = [&](const char* msg) { if (err) *err = msg; return false; };
+
+        uint32_t magic = 0; if (!read_pod(is, magic)) return fail("read CJ magic");
+        if (magic != CJ_MAGIC) return fail("bad CJ magic");
+
+        uint16_t ver = 0; if (!read_pod(is, ver)) return fail("read CJ version");
+        if (ver != CJ_VERSION) return fail("unsupported CJ version");
+
+        std::string cjId;
+        if (!read_string(is, cjId)) return fail("read CJID");
+
+        uint8_t prio = 0;
+        if (!read_pod(is, prio)) return fail("read Priority");
+
+        uint8_t st = 0;
+        if (!read_pod(is, st))   return fail("read State");
+
+        std::string failText;
+        if (!read_string(is, failText)) return fail("read failReason");
+
+        std::optional<std::chrono::system_clock::time_point> tQ, tS, tE;
+        if (!read_opt_time(is, tQ)) return fail("read tQueued");
+        if (!read_opt_time(is, tS)) return fail("read tStart");
+        if (!read_opt_time(is, tE)) return fail("read tEnd");
+
+        std::vector<std::string> pjIds;
+        if (!read_vec_str(is, pjIds)) return fail("read PJIDs");
+
+        // —— 写回对象(直接改成员,或通过 setter)——
+        // 若你有 setter:out.setId(...)/setPriority(...)/setState(...)/setFailReason(...)
+        out = CControlJob(cjId);
+        out.setPriority(prio);
+
+        // 直接恢复内部状态(若你要求走状态机,可在这里按合法过渡调用 queue()/start()/...)
+        // 简化:直接赋值(你在 CControlJob.cpp 内部,可访问私有成员)
+        struct Access : CControlJob {
+            using CControlJob::m_state;
+            using CControlJob::m_failReason;
+            using CControlJob::m_tQueued;
+            using CControlJob::m_tStart;
+            using CControlJob::m_tEnd;
+            using CControlJob::m_pjIds;
+        };
+        auto& a = reinterpret_cast<Access&>(out);
+        a.m_state = static_cast<CJState>(st);
+        a.m_failReason = std::move(failText);
+        a.m_tQueued = std::move(tQ);
+        a.m_tStart = std::move(tS);
+        a.m_tEnd = std::move(tE);
+        a.m_pjIds = std::move(pjIds);
+
+        return true;
+    }
 }
diff --git a/SourceCode/Bond/Servo/CControlJob.h b/SourceCode/Bond/Servo/CControlJob.h
index a9611a4..c5fdf60 100644
--- a/SourceCode/Bond/Servo/CControlJob.h
+++ b/SourceCode/Bond/Servo/CControlJob.h
@@ -36,6 +36,7 @@
     /// CControlJob:Control Job 管理类
     class CControlJob {
     public:
+        CControlJob();
         explicit CControlJob(std::string cjId);
         explicit CControlJob(CControlJob& src);
 
@@ -111,12 +112,18 @@
         static void clampString(std::string& s, size_t maxLen);
         static bool asciiPrintable(const std::string& s);
 
+        static constexpr uint32_t CJ_MAGIC = 0x434A5031; // "CJP1"
+        static constexpr uint16_t CJ_VERSION = 0x0001;
+
+        void serialize(std::ostream& os) const;
+        static bool deserialize(std::istream& is, CControlJob& out, std::string* err = nullptr);
+
     private:
         void markQueued();
         void markStart();
         void markEnd();
 
-    private:
+    protected:
         // —— 标识 & 配置 —— //
         std::string m_cjId;
         uint8_t     m_priority{ 5 }; // 缺省优先级(自定)
diff --git a/SourceCode/Bond/Servo/CMaster.cpp b/SourceCode/Bond/Servo/CMaster.cpp
index 81a0115..77b3a37 100644
--- a/SourceCode/Bond/Servo/CMaster.cpp
+++ b/SourceCode/Bond/Servo/CMaster.cpp
@@ -4,6 +4,8 @@
 #include <future>
 #include <vector>
 #include "RecipeManager.h"
+#include <fstream>
+#include "SerializeUtil.h"
 
 
 namespace SERVO {
@@ -1864,6 +1866,8 @@
 		}
 
 		m_processJobs = temp;
+		this->saveState();
+
 		return m_processJobs.size();
 	}
 
@@ -1919,6 +1923,9 @@
 			}
 		}
 		m_pControlJob->setPJs(temps);
+		this->saveState();
+
+
 		return 0;
 	}
 
@@ -1967,4 +1974,82 @@
 		return true;
 	}
 
+	bool CMaster::saveState() const
+	{
+		std::ofstream ofs(m_strStatePath, std::ios::binary);
+		if (!ofs) return false;
+
+		// 文件头
+		uint32_t magic = 0x4D415354; // 'MAST'
+		uint16_t version = 1;
+		ofs.write(reinterpret_cast<const char*>(&magic), sizeof(magic));
+		ofs.write(reinterpret_cast<const char*>(&version), sizeof(version));
+
+		// 保存 ControlJob
+		bool hasCJ = (m_pControlJob != nullptr);
+		ofs.write(reinterpret_cast<const char*>(&hasCJ), sizeof(hasCJ));
+		if (hasCJ) {
+			m_pControlJob->serialize(ofs);
+		}
+
+		// 保存 ProcessJob 列表
+		uint32_t count = static_cast<uint32_t>(m_processJobs.size());
+		ofs.write(reinterpret_cast<const char*>(&count), sizeof(count));
+		for (const auto& job : m_processJobs) {
+			job->serialize(ofs);
+		}
+
+		// 以后可以在这里追加新字段
+		return true;
+	}
+
+	bool CMaster::loadState(const std::string& path)
+	{
+		// 保存文件路径
+		m_strStatePath = path;
+
+
+		std::ifstream ifs(path, std::ios::binary);
+		if (!ifs) return false;
+
+		// 文件头
+		uint32_t magic = 0;
+		uint16_t version = 0;
+		ifs.read(reinterpret_cast<char*>(&magic), sizeof(magic));
+		ifs.read(reinterpret_cast<char*>(&version), sizeof(version));
+
+		if (magic != 0x4D415354) {
+			// 文件不合法
+			return false;
+		}
+
+		if (m_pControlJob != nullptr) {
+			delete m_pControlJob;
+			m_pControlJob = nullptr;
+		}
+
+		// 读取 ControlJob
+		bool hasCJ = false;
+		ifs.read(reinterpret_cast<char*>(&hasCJ), sizeof(hasCJ));
+		if (hasCJ) {
+			m_pControlJob = new CControlJob();
+			if (!CControlJob::deserialize(ifs, *m_pControlJob)) return false;
+		}
+
+
+		// 读取 ProcessJob 列表
+		uint32_t count = 0;
+		ifs.read(reinterpret_cast<char*>(&count), sizeof(count));
+		m_processJobs.clear();
+		for (uint32_t i = 0; i < count; i++) {
+			CProcessJob* pProcessJob = new CProcessJob();
+			if (!CProcessJob::deserialize(ifs, *pProcessJob)) return false;
+			m_processJobs.push_back(pProcessJob);
+		}
+
+		// 如果版本升级,可在这里判断 version 来加载新字段
+
+
+		return true;
+	}
 }
diff --git a/SourceCode/Bond/Servo/CMaster.h b/SourceCode/Bond/Servo/CMaster.h
index d43642d..097c2d6 100644
--- a/SourceCode/Bond/Servo/CMaster.h
+++ b/SourceCode/Bond/Servo/CMaster.h
@@ -111,6 +111,8 @@
         CProcessJob* getProcessJob(const std::string& id);
         int setControlJob(CControlJob& controlJob);
         CLoadPort* getPortWithCarrierId(const std::string& carrierId) const;
+        bool saveState() const;
+        bool loadState(const std::string& path);
 
     private:
         inline void lock() { EnterCriticalSection(&m_criticalSection); }
@@ -196,6 +198,7 @@
         bool m_bEnableAlarmReport;
         SERVO::CControlJob* m_pControlJob;
         std::vector<SERVO::CProcessJob*> m_processJobs;
+        std::string m_strStatePath;
     };
 }
 
diff --git a/SourceCode/Bond/Servo/Model.cpp b/SourceCode/Bond/Servo/Model.cpp
index 6ce35c5..8b854ee 100644
--- a/SourceCode/Bond/Servo/Model.cpp
+++ b/SourceCode/Bond/Servo/Model.cpp
@@ -397,6 +397,14 @@
 	m_master.setCacheFilepath((LPTSTR)(LPCTSTR)strMasterDataFile);
 	m_master.setCompareMapsBeforeProceeding(m_configuration.isCompareMapsBeforeProceeding());
 
+	// 加截Job
+	strMasterDataFile.Format(_T("%s\\MasterState.dat"), (LPTSTR)(LPCTSTR)m_strWorkDir);
+	std::string strPath = std::string((LPTSTR)(LPCTSTR)strMasterDataFile);
+	if (!m_master.loadState(strPath)) {
+		LOGE("<Master>加载MasterState.dat文件失败.");
+	}
+
+
 	// 加载警告信息
 	AlarmManager& alarmManager = AlarmManager::getInstance();
 	char szBuffer[MAX_PATH];
diff --git a/SourceCode/Bond/Servo/ProcessJob.cpp b/SourceCode/Bond/Servo/ProcessJob.cpp
index 16bfbb0..77dea9a 100644
--- a/SourceCode/Bond/Servo/ProcessJob.cpp
+++ b/SourceCode/Bond/Servo/ProcessJob.cpp
@@ -1,6 +1,9 @@
 #include "stdafx.h"
 #include "ProcessJob.h"
 #include <cctype>
+#include <fstream>
+#include "SerializeUtil.h"
+
 
 namespace SERVO {
     static inline std::string trimCopy(std::string s) {
@@ -8,6 +11,11 @@
         s.erase(s.begin(), std::find_if(s.begin(), s.end(), notspace));
         s.erase(std::find_if(s.rbegin(), s.rend(), notspace).base(), s.end());
         return s;
+    }
+
+    CProcessJob::CProcessJob()
+    {
+
     }
 
     CProcessJob::CProcessJob(std::string pjId)
@@ -257,4 +265,120 @@
             m_carriers.emplace_back(std::move(cs));
         }
     }
+
+    // --------- 核心:serialize/deserialize ---------
+    void CProcessJob::serialize(std::ostream& os) const {
+        // 头
+        write_pod(os, PJ_FILE_MAGIC);
+        write_pod(os, PJ_FILE_VERSION);
+
+        // 基本
+        write_string(os, m_pjId);
+        write_string(os, m_parentCjId);
+
+        // 配方
+        uint8_t recipeType = static_cast<uint8_t>(m_recipeMethod);
+        write_pod(os, m_recipeMethod);
+        write_string(os, m_recipeSpec);
+
+        // 物料(多 Carrier & Slot)
+        {
+            uint32_t n = static_cast<uint32_t>(m_carriers.size());
+            write_pod(os, n);
+            for (const auto& cs : m_carriers) {
+                write_string(os, cs.carrierId);
+                write_vec<uint8_t>(os, cs.slots);
+            }
+        }
+
+        // 参数
+        {
+            uint32_t n = static_cast<uint32_t>(m_params.size());
+            write_pod(os, n);
+            for (const auto& p : m_params) {
+                write_string(os, p.name);
+                write_string(os, p.value);
+            }
+        }
+
+        // 暂停事件
+        write_vec<uint32_t>(os, m_pauseEvents);
+
+        // 启动策略 & 状态
+        uint8_t startPolicy = static_cast<uint8_t>(m_startPolicy);
+        uint8_t st = static_cast<uint8_t>(m_state);
+        write_pod(os, startPolicy);
+        write_pod(os, st);
+
+        // 失败原因
+        write_string(os, m_failReason);
+
+        // 时间戳
+        write_opt_time(os, m_tQueued);
+        write_opt_time(os, m_tStart);
+        write_opt_time(os, m_tEnd);
+    }
+
+    bool CProcessJob::deserialize(std::istream& is, CProcessJob& out, std::string* err) {
+        auto fail = [&](const char* msg) { if (err) *err = msg; return false; };
+
+        uint32_t magic = 0; if (!read_pod(is, magic)) return fail("read magic failed");
+        if (magic != PJ_FILE_MAGIC) return fail("bad magic");
+
+        uint16_t ver = 0; if (!read_pod(is, ver)) return fail("read version failed");
+        if (ver != PJ_FILE_VERSION) return fail("unsupported version");
+
+        // 基本
+        if (!read_string(is, out.m_pjId))        return fail("read pjId");
+        if (!read_string(is, out.m_parentCjId))  return fail("read parentCjId");
+
+        // 配方
+        uint8_t recipeType = 0; if (!read_pod(is, recipeType)) return fail("read recipeType");
+        out.m_recipeMethod = static_cast<RecipeMethod>(recipeType);
+        if (!read_string(is, out.m_recipeSpec)) return fail("read recipeSpec");
+
+        // 物料
+        {
+            uint32_t n = 0; if (!read_pod(is, n)) return fail("read carriers count");
+            out.m_carriers.clear(); out.m_carriers.reserve(n);
+            for (uint32_t i = 0; i < n; ++i) {
+                CarrierSlotInfo cs;
+                if (!read_string(is, cs.carrierId)) return fail("read carrierId");
+                if (!read_vec<uint8_t>(is, cs.slots)) return fail("read slots");
+                out.m_carriers.emplace_back(std::move(cs));
+            }
+        }
+
+        // 参数
+        {
+            uint32_t n = 0; if (!read_pod(is, n)) return fail("read params count");
+            out.m_params.clear(); out.m_params.reserve(n);
+            for (uint32_t i = 0; i < n; ++i) {
+                PJParam p;
+                if (!read_string(is, p.name))  return fail("read param name");
+                if (!read_string(is, p.value)) return fail("read param value");
+                out.m_params.emplace_back(std::move(p));
+            }
+        }
+
+        // 暂停事件
+        if (!read_vec<uint32_t>(is, out.m_pauseEvents)) return fail("read pauseEvents");
+
+        // 启动策略 & 状态
+        uint8_t startPolicy = 0, st = 0;
+        if (!read_pod(is, startPolicy)) return fail("read startPolicy");
+        if (!read_pod(is, st))          return fail("read state");
+        out.m_startPolicy = static_cast<StartPolicy>(startPolicy);
+        out.m_state = static_cast<PJState>(st);
+
+        // 失败原因
+        if (!read_string(is, out.m_failReason)) return fail("read failReason");
+
+        // 时间戳
+        if (!read_opt_time(is, out.m_tQueued)) return fail("read tQueued");
+        if (!read_opt_time(is, out.m_tStart))  return fail("read tStart");
+        if (!read_opt_time(is, out.m_tEnd))    return fail("read tEnd");
+
+        return true;
+    }
 }
diff --git a/SourceCode/Bond/Servo/ProcessJob.h b/SourceCode/Bond/Servo/ProcessJob.h
index da5db9b..062bd8b 100644
--- a/SourceCode/Bond/Servo/ProcessJob.h
+++ b/SourceCode/Bond/Servo/ProcessJob.h
@@ -85,6 +85,7 @@
     class CProcessJob {
     public:
         // —— 构造 / 基本设置 ——
+        CProcessJob();
         explicit CProcessJob(std::string pjId);
 
         const std::string& id() const noexcept { return m_pjId; }
@@ -157,6 +158,15 @@
         bool usesCarrierSlots() const noexcept { return !m_carriers.empty(); }
 
 
+    public:
+        // ====== 版本头常量(建议保留,便于兼容)======
+        static constexpr uint32_t PJ_FILE_MAGIC = 0x504A4A31; // "PJJ1"
+        static constexpr uint16_t PJ_FILE_VERSION = 0x0001;
+
+        // ====== 流式序列化接口 ======
+        void serialize(std::ostream& os) const;
+        static bool deserialize(std::istream& is, CProcessJob& out, std::string* err = nullptr);
+
     private:
         // 内部状态转移帮助
         void markQueued();
diff --git a/SourceCode/Bond/Servo/SerializeUtil.h b/SourceCode/Bond/Servo/SerializeUtil.h
new file mode 100644
index 0000000..fe5fdf7
--- /dev/null
+++ b/SourceCode/Bond/Servo/SerializeUtil.h
@@ -0,0 +1,72 @@
+#pragma once
+#include <fstream>
+
+
+// --------- 私有/内部:读写工具 ---------
+namespace SERVO {
+    template<typename T>
+    inline void write_pod(std::ostream& os, const T& v) {
+        os.write(reinterpret_cast<const char*>(&v), sizeof(T));
+    }
+    template<typename T>
+    inline bool read_pod(std::istream& is, T& v) {
+        return bool(is.read(reinterpret_cast<char*>(&v), sizeof(T)));
+    }
+
+    inline void write_string(std::ostream& os, const std::string& s) {
+        uint32_t n = static_cast<uint32_t>(s.size());
+        write_pod(os, n);
+        if (n) os.write(s.data(), n);
+    }
+    inline bool read_string(std::istream& is, std::string& s) {
+        uint32_t n = 0; if (!read_pod(is, n)) return false;
+        s.resize(n);
+        if (n) return bool(is.read(&s[0], n));
+        return true;
+    }
+
+    template<typename T>
+    inline void write_vec(std::ostream& os, const std::vector<T>& v) {
+        uint32_t n = static_cast<uint32_t>(v.size());
+        write_pod(os, n);
+        if (n) os.write(reinterpret_cast<const char*>(v.data()), sizeof(T) * n);
+    }
+    template<typename T>
+    inline bool read_vec(std::istream& is, std::vector<T>& v) {
+        uint32_t n = 0; if (!read_pod(is, n)) return false;
+        v.resize(n);
+        if (n) return bool(is.read(reinterpret_cast<char*>(v.data()), sizeof(T) * n));
+        return true;
+    }
+
+    // vector<string> 特化写读
+    inline void write_vec_str(std::ostream& os, const std::vector<std::string>& v) {
+        uint32_t n = static_cast<uint32_t>(v.size());
+        write_pod(os, n);
+        for (const auto& s : v) write_string(os, s);
+    }
+    inline bool read_vec_str(std::istream& is, std::vector<std::string>& v) {
+        uint32_t n = 0; if (!read_pod(is, n)) return false;
+        v.clear(); v.reserve(n);
+        for (uint32_t i = 0; i < n; ++i) { std::string s; if (!read_string(is, s)) return false; v.emplace_back(std::move(s)); }
+        return true;
+    }
+
+    // optional<time_point> → bool + int64 (ms since epoch)
+    inline void write_opt_time(std::ostream& os, const std::optional<std::chrono::system_clock::time_point>& tp) {
+        uint8_t has = tp.has_value() ? 1 : 0;
+        write_pod(os, has);
+        if (has) {
+            auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(tp->time_since_epoch()).count();
+            int64_t v = static_cast<int64_t>(ms);
+            write_pod(os, v);
+        }
+    }
+    inline bool read_opt_time(std::istream& is, std::optional<std::chrono::system_clock::time_point>& tp) {
+        uint8_t has = 0; if (!read_pod(is, has)) return false;
+        if (!has) { tp.reset(); return true; }
+        int64_t v = 0; if (!read_pod(is, v)) return false;
+        tp = std::chrono::system_clock::time_point(std::chrono::milliseconds(v));
+        return true;
+    }
+}
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj b/SourceCode/Bond/Servo/Servo.vcxproj
index 7bf97d3..b798ed7 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj
+++ b/SourceCode/Bond/Servo/Servo.vcxproj
@@ -333,6 +333,7 @@
     <ClInclude Include="Resource.h" />
     <ClInclude Include="SECSRuntimeManager.h" />
     <ClInclude Include="SecsTestDlg.h" />
+    <ClInclude Include="SerializeUtil.h" />
     <ClInclude Include="Servo.h" />
     <ClInclude Include="ServoCommo.h" />
     <ClInclude Include="ServoDlg.h" />
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj.filters b/SourceCode/Bond/Servo/Servo.vcxproj.filters
index f96e572..1d8a259 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj.filters
+++ b/SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -361,6 +361,7 @@
     <ClInclude Include="CPageCollectionEvent.h" />
     <ClInclude Include="ProcessJob.h" />
     <ClInclude Include="CControlJob.h" />
+    <ClInclude Include="SerializeUtil.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Servo.rc" />

--
Gitblit v1.9.3