From 43c7dc211f10851480352b12bd01f3443079bb01 Mon Sep 17 00:00:00 2001
From: mrDarker <mr.darker@163.com>
Date: 星期二, 26 八月 2025 09:09:21 +0800
Subject: [PATCH] Merge branch 'clh' into liuyang
---
SourceCode/Bond/Servo/ProcessJob.cpp | 433 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 433 insertions(+), 0 deletions(-)
diff --git a/SourceCode/Bond/Servo/ProcessJob.cpp b/SourceCode/Bond/Servo/ProcessJob.cpp
new file mode 100644
index 0000000..b672e27
--- /dev/null
+++ b/SourceCode/Bond/Servo/ProcessJob.cpp
@@ -0,0 +1,433 @@
+#include "stdafx.h"
+#include "ProcessJob.h"
+#include <cctype>
+#include <fstream>
+#include "SerializeUtil.h"
+
+
+namespace SERVO {
+ static inline std::string trimCopy(std::string s) {
+ auto notspace = [](int ch) { return !std::isspace(ch); };
+ 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)
+ : m_pjId(trimCopy(pjId))
+ {
+ clampString(m_pjId, MAX_ID_LEN);
+ }
+
+ void CProcessJob::setParentCjId(std::string cjId) {
+ m_parentCjId = trimCopy(cjId);
+ clampString(m_parentCjId, MAX_ID_LEN);
+ }
+
+ void CProcessJob::setRecipe(RecipeMethod method, std::string spec) {
+ m_recipeMethod = method;
+ m_recipeSpec = trimCopy(spec);
+ clampString(m_recipeSpec, MAX_ID_LEN);
+ }
+
+ void CProcessJob::addParam(std::string name, std::string value) {
+ name = trimCopy(name);
+ value = trimCopy(value);
+ clampString(name, MAX_PARAM_K);
+ clampString(value, MAX_PARAM_V);
+ m_params.push_back({ std::move(name), std::move(value) });
+ }
+
+ void CProcessJob::setParams(std::vector<PJParam> params) {
+ m_params.clear();
+ m_params.reserve(params.size());
+ for (auto& p : params) addParam(std::move(p.name), std::move(p.value));
+ }
+
+ void CProcessJob::addPauseEvent(uint32_t ceid) {
+ if (ceid) m_pauseEvents.push_back(ceid);
+ std::sort(m_pauseEvents.begin(), m_pauseEvents.end());
+ m_pauseEvents.erase(std::unique(m_pauseEvents.begin(), m_pauseEvents.end()), m_pauseEvents.end());
+ }
+
+ void CProcessJob::setPauseEvents(std::vector<uint32_t> ceids) {
+ m_pauseEvents = std::move(ceids);
+ std::sort(m_pauseEvents.begin(), m_pauseEvents.end());
+ m_pauseEvents.erase(std::unique(m_pauseEvents.begin(), m_pauseEvents.end()), m_pauseEvents.end());
+ }
+
+ const std::vector<CProcessJob::ValidationIssue>& CProcessJob::issues()
+ {
+ return m_issues;
+ }
+
+ bool CProcessJob::validate(const IResourceView& rv)
+ {
+ m_issues.clear();
+
+ // 让 add 同时支持 const char* 和 std::string
+ auto add = [&](uint32_t code, std::string msg) {
+ m_issues.push_back({ code, std::move(msg) });
+ };
+
+ if (!rv.isProcessJobsEmpty()) {
+ add(1000, "ProcessJobs Conflict!");
+ }
+
+ // —— 基本 / 标识 ——
+ if (m_pjId.empty()) add(1001, "PJID empty");
+ if (!asciiPrintable(m_pjId)) add(1002, "PJID has non-printable chars");
+
+ // if (m_parentCjId.empty()) add(1010, "Parent CJID empty");
+
+ // —— 配方(RCPSPEC / PPID)——
+ if (m_recipeSpec.empty()) add(1020, "Recipe spec (PPID) empty");
+ else if (!rv.recipeExists(m_recipeSpec)) {
+ add(1021, "PPID not found: " + m_recipeSpec);
+ }
+
+ // —— 配方方法 vs 参数 —— 1=NoTuning 禁止带参数;2=WithTuning 允许/可选
+ if (m_recipeMethod == RecipeMethod::NoTuning && !m_params.empty()) {
+ add(1022, "Params not allowed when PRRECIPEMETHOD=1 (NoTuning)");
+ }
+
+ // —— 物料选择校验 ——(二选一:Carrier+Slots 或 MIDs;两者都不填则错误)
+ const bool hasCarrierSlots = !m_carriers.empty();
+ if (hasCarrierSlots) {
+ // {L:n { CARRIERID {L:j SLOTID} }}
+ for (const auto& cs : m_carriers) {
+ if (cs.carrierId.empty()) {
+ add(1030, "CarrierID empty");
+ continue;
+ }
+ if (!rv.carrierPresent(cs.carrierId)) {
+ add(1031, "Carrier not present: " + cs.carrierId);
+ }
+ if (cs.slots.empty()) {
+ add(1032, "No slots specified for carrier: " + cs.carrierId);
+ }
+ for (auto s : cs.slots) {
+ if (s == 0) {
+ add(1033, "Slot 0 is invalid for carrier: " + cs.carrierId);
+ continue;
+ }
+ if (!rv.slotUsable(cs.carrierId, s)) {
+ add(1034, "Slot unusable: carrier=" + cs.carrierId + " slot=" + std::to_string(s));
+ }
+ }
+ }
+ }
+ else {
+ add(1035, "No material selection provided (neither Carrier/Slots nor MIDs)");
+ }
+
+ // —— 暂停事件(PRPAUSEEVENTID 列表)——
+ for (auto ceid : m_pauseEvents) {
+ if (!rv.ceidDefined(ceid)) {
+ add(1050, "Pause CEID unknown: " + std::to_string(ceid));
+ }
+ }
+
+ return m_issues.empty();
+ }
+
+ // —— 状态机 ——
+ // 规则可按你们协议微调
+ bool CProcessJob::queue() {
+ if (m_state != PJState::NoState) return false;
+ markQueued();
+ return true;
+ }
+
+ bool CProcessJob::enterSettingUp() {
+ if (m_state != PJState::Queued) return false;
+ m_state = PJState::SettingUp;
+ return true;
+ }
+
+ bool CProcessJob::start() {
+ if (m_state != PJState::Queued && m_state != PJState::SettingUp && m_state != PJState::Paused)
+ return false;
+ if (!m_tStart.has_value()) markStart();
+ m_state = PJState::InProcess;
+ return true;
+ }
+
+ bool CProcessJob::pause() {
+ if (m_state != PJState::InProcess) return false;
+ m_state = PJState::Paused;
+ return true;
+ }
+
+ bool CProcessJob::resume() {
+ if (m_state != PJState::Paused) return false;
+ m_state = PJState::InProcess;
+ return true;
+ }
+
+ bool CProcessJob::complete() {
+ if (m_state != PJState::InProcess && m_state != PJState::Paused) return false;
+ m_state = PJState::Completed;
+ markEnd();
+ return true;
+ }
+
+ bool CProcessJob::abort() {
+ if (m_state == PJState::Completed || m_state == PJState::Aborted || m_state == PJState::Failed)
+ return false;
+ m_state = PJState::Aborted;
+ markEnd();
+ return true;
+ }
+
+ bool CProcessJob::fail(std::string reason) {
+ m_failReason = trimCopy(reason);
+ clampString(m_failReason, 128);
+ m_state = PJState::Failed;
+ markEnd();
+ return true;
+ }
+
+ // —— 时间戳 & 工具 ——
+ void CProcessJob::markQueued() {
+ m_state = PJState::Queued;
+ m_tQueued = std::chrono::system_clock::now();
+ }
+
+ void CProcessJob::markStart() {
+ m_tStart = std::chrono::system_clock::now();
+ }
+
+ void CProcessJob::markEnd() {
+ m_tEnd = std::chrono::system_clock::now();
+ }
+
+ void CProcessJob::clampString(std::string& s, size_t maxLen) {
+ if (s.size() > maxLen) s.resize(maxLen);
+ }
+
+ bool CProcessJob::asciiPrintable(const std::string& s) {
+ return std::all_of(s.begin(), s.end(), [](unsigned char c) {
+ return c >= 0x20 && c <= 0x7E;
+ });
+ }
+
+ void CProcessJob::setCarriers(std::vector<CarrierSlotInfo> carriers)
+ {
+ // 统一通过 addCarrier 做规范化(去空白、截断、去重、合并同 carrier)
+ m_carriers.clear();
+ m_carriers.reserve(carriers.size());
+ for (auto& cs : carriers) {
+ addCarrier(std::move(cs.carrierId), std::move(cs.slots));
+ }
+ }
+
+ void CProcessJob::addCarrier(std::string carrierId, std::vector<uint8_t> slots)
+ {
+ // 1) 规范化 carrierId:去空白 + 长度限制
+ carrierId = trimCopy(std::move(carrierId));
+ clampString(carrierId, MAX_ID_LEN);
+ if (carrierId.empty()) {
+ // 空 ID 直接忽略(也可以选择抛异常/记录日志,看你项目风格)
+ return;
+ }
+
+ // 2) 规范化 slots:去 0、排序、去重
+ // 注:SLOTID 按 1..N,0 视为非法/占位
+ slots.erase(std::remove(slots.begin(), slots.end(), 0), slots.end());
+ std::sort(slots.begin(), slots.end());
+ slots.erase(std::unique(slots.begin(), slots.end()), slots.end());
+ if (slots.empty()) {
+ // 没有有效卡位就不追加
+ return;
+ }
+
+ // 3) 如果已存在同名载具,则合并 slot 列表
+ auto it = std::find_if(m_carriers.begin(), m_carriers.end(),
+ [&](const CarrierSlotInfo& cs) { return cs.carrierId == carrierId; });
+
+ if (it != m_carriers.end()) {
+ // 合并
+ it->slots.insert(it->slots.end(), slots.begin(), slots.end());
+ std::sort(it->slots.begin(), it->slots.end());
+ it->slots.erase(std::unique(it->slots.begin(), it->slots.end()), it->slots.end());
+ }
+ else {
+ // 新增
+ CarrierSlotInfo cs;
+ cs.carrierId = std::move(carrierId);
+ cs.slots = std::move(slots);
+ 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;
+ }
+
+ std::string CProcessJob::getStateText()
+ {
+ switch (m_state)
+ {
+ case SERVO::PJState::NoState:
+ return "NoState";
+ break;
+ case SERVO::PJState::Queued:
+ return "Queued";
+ break;
+ case SERVO::PJState::SettingUp:
+ return "SettingUp";
+ break;
+ case SERVO::PJState::InProcess:
+ return "InProcess";
+ break;
+ case SERVO::PJState::Paused:
+ return "Queued";
+ break;
+ case SERVO::PJState::Aborting:
+ return "Aborting";
+ break;
+ case SERVO::PJState::Completed:
+ return "Queued";
+ break;
+ case SERVO::PJState::Aborted:
+ return "Aborted";
+ break;
+ case SERVO::PJState::Failed:
+ return "Failed";
+ break;
+ default:
+ break;
+ }
+
+ return "";
+ }
+
+ CarrierSlotInfo* CProcessJob::getCarrier(std::string& strId)
+ {
+ for (auto& item : m_carriers) {
+ if (item.carrierId.compare(strId) == 0) {
+ return &item;
+ }
+ }
+
+ return nullptr;
+ }
+}
--
Gitblit v1.9.3