mrDarker
2025-08-26 43c7dc211f10851480352b12bd01f3443079bb01
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;
    }
}