#include "stdafx.h"
|
#include "CControlJob.h"
|
#include <cctype>
|
#include "SerializeUtil.h"
|
|
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;
|
}
|
|
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;
|
}
|
|
void CControlJob::setId(std::string& id)
|
{
|
m_cjId = trimCopy(id);
|
clampString(m_cjId, MAX_ID_LEN);
|
}
|
|
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::addPjPointer(CProcessJob* pj)
|
{
|
for (auto item : m_pjs) {
|
if (item->id().compare(pj->id()) == 0) return false;
|
}
|
|
m_pjs.push_back(pj);
|
return true;
|
}
|
|
bool CControlJob::removePjPointer(const std::string& id)
|
{
|
for(auto iter = m_pjs.begin(); iter != m_pjs.end(); ++iter) {
|
if ((*iter)->id().compare(id) == 0) {
|
m_pjs.erase(iter);
|
return true;
|
}
|
}
|
|
return false;
|
}
|
|
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;
|
}
|
|
void CControlJob::clearIssues()
|
{
|
m_issues.clear();
|
}
|
|
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(std::string reason) {
|
if (m_state == CJState::Completed || m_state == CJState::Aborted || m_state == CJState::Failed)
|
return false;
|
m_failReason = trimCopy(reason);
|
clampString(m_failReason, 128);
|
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);
|
|
out.m_state = static_cast<CJState>(st);
|
out.m_failReason = std::move(failText);
|
out.m_tQueued = std::move(tQ);
|
out.m_tStart = std::move(tS);
|
out.m_tEnd = std::move(tE);
|
out.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 "";
|
}
|
}
|