#include "stdafx.h"
|
#include "ProcessJob.h"
|
#include <cctype>
|
#include <fstream>
|
#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<unsigned char>(c)); }));
|
s.erase(std::find_if(s.rbegin(), s.rend(),
|
[](char c) { return !std::isspace(static_cast<unsigned char>(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<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() 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() {
|
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, recipeType);
|
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 "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;
|
}
|
}
|