| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #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 ""; |
| | | } |
| | | } |