mrDarker
2025-08-26 43c7dc211f10851480352b12bd01f3443079bb01
SourceCode/Bond/Servo/CControlJob.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,336 @@
#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); };
    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;
}
namespace SERVO {
    CControlJob::CControlJob()
    {
    }
    CControlJob::CControlJob(std::string cjId)
        : m_cjId(trimCopy(std::move(cjId)))
    {
        clampString(m_cjId, MAX_ID_LEN);
    }
    CControlJob::CControlJob(CControlJob& src)
    {
        m_cjId = src.m_cjId;
        clampString(m_cjId, MAX_ID_LEN);
        m_priority = src.m_priority;
        m_pjIds = src.m_pjIds;
        m_state = src.m_state;
        m_failReason = src.m_failReason;
        m_tQueued = src.m_tQueued;
        m_tStart = src.m_tStart;
        m_tEnd = src.m_tEnd;
    }
    bool CControlJob::addPJ(const std::string& pjId) {
        if (pjId.empty()) return false;
        auto id = pjId;
        auto it = std::find(m_pjIds.begin(), m_pjIds.end(), id);
        if (it != m_pjIds.end()) return false;
        clampString(id, MAX_ID_LEN);
        m_pjIds.push_back(std::move(id));
        return true;
    }
    bool CControlJob::addPJs(const std::vector<std::string>& ids) {
        bool added = false;
        for (auto& s : ids) added |= addPJ(s);
        return added;
    }
    bool CControlJob::setPJs(const std::vector<CProcessJob*>& pjs)
    {
        m_pjs = pjs;
        return true;
    }
    bool CControlJob::removePJ(const std::string& pjId) {
        auto it = std::find(m_pjIds.begin(), m_pjIds.end(), pjId);
        if (it == m_pjIds.end()) return false;
        m_pjIds.erase(it);
        return true;
    }
    bool CControlJob::containsPJ(const std::string& pjId) const {
        return std::find(m_pjIds.begin(), m_pjIds.end(), pjId) != m_pjIds.end();
    }
    const std::vector<CControlJob::ValidationIssue>& CControlJob::issues()
    {
        return m_issues;
    }
    bool CControlJob::validateForCreate(
            const std::function<bool(uint32_t& code, std::string& msg)>& canCreateCjFn,
            const std::function<bool(const std::string&)>& getPjExistsFn,
            const std::function<bool(const std::string&)>& canJoinFn
        )
    {
        m_issues.clear();
        auto add = [&](uint32_t code, std::string msg) { m_issues.push_back({ code, std::move(msg) }); };
        // æ˜¯å¦èƒ½åˆ›å»ºCJ, ç”±ä¸Šå±‚根据当前任务,机器状态等检验
        uint32_t cc;
        std::string mm;
        if (!canCreateCjFn(cc, mm)) {
            add(cc, mm);
        }
        // CJID åŸºç¡€æ ¡éªŒ
        if (m_cjId.empty())           add(1101, "CJID empty");
        if (!asciiPrintable(m_cjId))  add(1102, "CJID has non-printable chars");
        // PJ åˆ—表校验
        if (m_pjIds.empty())          add(1110, "PRJOBLIST empty");
        for (const auto& pj : m_pjIds) {
            if (!getPjExistsFn(pj))   add(1111, "PJ not found: " + pj);
            else if (!canJoinFn(pj))  add(1112, "PJ not joinable: " + pj);
        }
        return m_issues.empty();
    }
    // åº”用创建/更新(用于 S14F9 â†’ S14F10 è·¯å¾„)
    CControlJob::CreateResult
        CControlJob::applyCreate(
            const CreateRequest& req,
            const std::function<bool(const std::string&)>& getPjExistsFn,
            const std::function<bool(const std::string&)>& canJoinFn
        )
    {
        CreateResult r;
        // è¦†ç›–优先级(如提供)
        if (req.priority.has_value()) {
            m_priority = *req.priority;
        }
        // é€ PJ åˆ¤å®š
        for (const auto& pjIdRaw : req.requestedPjIds) {
            std::string pjId = trimCopy(pjIdRaw);
            clampString(pjId, MAX_ID_LEN);
            if (!getPjExistsFn(pjId)) {
                r.errors.push_back({ 2001, "PRJOBLIST: " + pjId + " not found" });
                continue;
            }
            if (!canJoinFn(pjId)) {
                r.errors.push_back({ 2002, "PRJOBLIST: " + pjId + " not joinable (state)" });
                continue;
            }
            if (containsPJ(pjId)) {
                // å·²åœ¨åˆ—表,视作成功(幂等)
                r.acceptedPjIds.push_back(pjId);
                continue;
            }
            // åŠ å…¥ CJ
            m_pjIds.push_back(pjId);
            r.acceptedPjIds.push_back(std::move(pjId));
        }
        // å½’å¹¶ ACK
        if (r.errors.empty())               r.objack = 0; // å…¨æˆåŠŸ
        else if (!r.acceptedPjIds.empty())  r.objack = 1; // éƒ¨åˆ†æˆåŠŸ
        else                                r.objack = 2; // å…¨å¤±è´¥
        return r;
    }
    // â€”— çŠ¶æ€æœº â€”— //
    bool CControlJob::queue() {
        if (m_state != CJState::NoState) return false;
        markQueued();
        return true;
    }
    bool CControlJob::start() {
        if (m_state != CJState::Queued) return false;
        m_state = CJState::Executing;
        if (!m_tStart.has_value()) markStart();
        return true;
    }
    bool CControlJob::pause() {
        if (m_state != CJState::Executing) return false;
        m_state = CJState::Paused;
        return true;
    }
    bool CControlJob::resume() {
        if (m_state != CJState::Paused) return false;
        m_state = CJState::Executing;
        return true;
    }
    bool CControlJob::complete() {
        if (m_state != CJState::Executing && m_state != CJState::Paused) return false;
        m_state = CJState::Completed;
        markEnd();
        return true;
    }
    bool CControlJob::abort() {
        if (m_state == CJState::Completed || m_state == CJState::Aborted || m_state == CJState::Failed)
            return false;
        m_state = CJState::Aborted;
        markEnd();
        return true;
    }
    bool CControlJob::fail(std::string reason) {
        m_failReason = trimCopy(reason);
        clampString(m_failReason, 128);
        m_state = CJState::Failed;
        markEnd();
        return true;
    }
    // â€”— èšåˆå®Œæˆåˆ¤æ–­ â€”— //
    bool CControlJob::tryAggregateComplete(
        const std::function<bool(const std::string&)>& isPjCompletedFn
    ) {
        if (m_pjIds.empty()) return false;
        for (const auto& pj : m_pjIds) {
            if (!isPjCompletedFn(pj)) return false;
        }
        // æ‰€æœ‰ PJ å·²å®Œæˆ â†’ CJ å®Œæˆ
        return complete();
    }
    // â€”— æ—¶é—´æˆ³ â€”— //
    void CControlJob::markQueued() { m_state = CJState::Queued;    m_tQueued = std::chrono::system_clock::now(); }
    void CControlJob::markStart() { m_tStart = std::chrono::system_clock::now(); }
    void CControlJob::markEnd() { m_tEnd = std::chrono::system_clock::now(); }
    // â€”— å·¥å…· â€”— //
    void CControlJob::clampString(std::string& s, size_t maxLen) {
        if (s.size() > maxLen) s.resize(maxLen);
    }
    bool CControlJob::asciiPrintable(const std::string& s) {
        return std::all_of(s.begin(), s.end(), [](unsigned char c) {
            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;
    }
    std::string CControlJob::getStateText()
    {
        switch (m_state)
        {
        case SERVO::CJState::NoState:
            return "NoState";
            break;
        case SERVO::CJState::Queued:
            return "Queued";
            break;
        case SERVO::CJState::Executing:
            return "Executing";
            break;
        case SERVO::CJState::Paused:
            return "Paused";
            break;
        case SERVO::CJState::Completed:
            return "Completed";
            break;
        case SERVO::CJState::Aborted:
            return "Aborted";
            break;
        case SERVO::CJState::Failed:
            return "Failed";
            break;
        default:
            break;
        }
        return "";
    }
}