#include "stdafx.h" #include "ProcessJob.h" #include #include #include "SerializeUtil.h" namespace SERVO { static inline std::string trimCopy(std::string s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char c) { return !std::isspace(static_cast(c)); })); s.erase(std::find_if(s.rbegin(), s.rend(), [](char c) { return !std::isspace(static_cast(c)); }).base(), s.end()); return s; } CProcessJob::CProcessJob() { } CProcessJob::CProcessJob(std::string pjId) : m_pjId(trimCopy(pjId)) { clampString(m_pjId, MAX_ID_LEN); } void CProcessJob::setId(std::string& id) { m_pjId = trimCopy(id); 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 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 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::issues() const { 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(std::string reason) { if (m_state == PJState::Completed || m_state == PJState::Aborted || m_state == PJState::Failed) return false; m_failReason = trimCopy(reason); clampString(m_failReason, 128); 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 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 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(m_recipeMethod); write_pod(os, recipeType); write_string(os, m_recipeSpec); // ÎïÁÏ£¨¶à Carrier & Slot£© { uint32_t n = static_cast(m_carriers.size()); write_pod(os, n); for (const auto& cs : m_carriers) { write_string(os, cs.carrierId); write_vec(os, cs.slots); } } // ²ÎÊý { uint32_t n = static_cast(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(os, m_pauseEvents); // Æô¶¯²ßÂÔ & ״̬ uint8_t startPolicy = static_cast(m_startPolicy); uint8_t st = static_cast(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(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(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(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); out.m_state = static_cast(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 "Paused"; break; case SERVO::PJState::Aborting: return "Aborting"; break; case SERVO::PJState::Completed: return "Completed"; break; case SERVO::PJState::Aborted: return "Aborted"; break; case SERVO::PJState::Failed: return "Failed"; break; default: break; } return ""; } CarrierSlotInfo* CProcessJob::getCarrier(const std::string& strId) { for (auto& item : m_carriers) { if (item.carrierId.compare(strId) == 0) { return &item; } } return nullptr; } }