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