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