From a2209cc432cb9c80779d83e51ef090f782e8404a Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期四, 14 八月 2025 17:30:52 +0800
Subject: [PATCH] 1.Create PJ功能实现;
---
SourceCode/Bond/EAPSimulator/ProcessJob.h | 198 ++++++++
SourceCode/Bond/Servo/Servo.vcxproj | 3
SourceCode/Bond/Servo/HsmsPassive.h | 4
SourceCode/Bond/Servo/ProcessJob.h | 203 ++++++++
SourceCode/Bond/Servo/ProcessJob.cpp | 260 ++++++++++
SourceCode/Bond/EAPSimulator/ProcessJob.cpp | 252 ++++++++++
SourceCode/Bond/EAPSimulator/CHsmsActive.h | 3
SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h | 1
SourceCode/Bond/Servo/HsmsPassive.cpp | 108 ++++
SourceCode/Bond/EAPSimulator/EAPSimulator.rc | 0
SourceCode/Bond/Servo/CMaster.h | 16
SourceCode/Bond/Servo/Model.cpp | 6
SourceCode/Bond/Servo/CLoadPort.cpp | 18
SourceCode/Bond/EAPSimulator/EAPSimulator.vcxproj.filters | 12
SourceCode/Bond/EAPSimulator/CHsmsActive.cpp | 38 +
SourceCode/Bond/EAPSimulator/Resource.h | 8
SourceCode/Bond/Servo/Servo.vcxproj.filters | 2
SourceCode/Bond/EAPSimulator/CPJsDlg.h | 35 +
SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp | 9
SourceCode/Bond/EAPSimulator/CPJsDlg.cpp | 161 ++++++
SourceCode/Bond/EAPSimulator/EAPSimulator.vcxproj | 5
SourceCode/Bond/Servo/CMaster.cpp | 74 +++
Document/Panel Bonder八零联合 SecsTest CheckList_v3.0.xlsx | 0
23 files changed, 1,409 insertions(+), 7 deletions(-)
diff --git "a/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx" "b/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx"
index e062180..16c9afe 100644
--- "a/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx"
+++ "b/Document/Panel Bonder\345\205\253\351\233\266\350\201\224\345\220\210 SecsTest CheckList_v3.0.xlsx"
Binary files differ
diff --git a/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp b/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp
index b084312..b094a6a 100644
--- a/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp
+++ b/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp
@@ -3,6 +3,8 @@
#include "Log.h"
+static unsigned int DATAID = 1;
+
CHsmsActive::CHsmsActive()
{
m_listener = {};
@@ -343,6 +345,42 @@
return hsmsCarrierActionRequest(DATAID, "CarrierRelease", pszCarrierId, PTN);
}
+int CHsmsActive::hsmsPRJobMultiCreate(std::vector<SERVO::CProcessJob*>& pjs)
+{
+ IMessage* pMessage = nullptr;
+ int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 16 | REPLY, 15, ++m_nSystemByte);
+ char szMF[32] = {14};
+ pMessage->getBody()->addU4Item(++DATAID, "DATAID");
+ auto itemPjs = pMessage->getBody()->addItem();
+ for (auto pj : pjs) {
+ auto itemPj = itemPjs->addItem();
+ itemPj->addItem(pj->id().c_str(), "PRJOBID");
+ itemPj->addBinaryItem(szMF, 1, "MF");
+ auto itemCarriers = itemPj->addItem();
+ for (auto c : pj->carriers()) {
+ auto itemCarrier = itemCarriers->addItem();
+ itemCarrier->addItem(c.carrierId.c_str(), "CARRIERID");
+ auto itemSlots = itemCarrier->addItem();
+ for (auto s : c.slots) {
+ itemSlots->addU1Item(s, "SLOTID");
+ }
+ }
+
+ auto recipeItems = itemPj->addItem();
+ recipeItems->addU1Item(1, "PRRECIPEMETHOD");
+ recipeItems->addItem(pj->recipeSpec().c_str(), "RCPSPEC");
+ recipeItems->addItem();
+
+ itemPj->addBoolItem(false, "PRPROCESSSTART");
+ itemPj->addItem();
+ }
+
+ m_pActive->sendMessage(pMessage);
+ HSMS_Destroy1Message(pMessage);
+
+ return 0;
+}
+
int CHsmsActive::replyAck0(IMessage* pMessage)
{
return 0;
diff --git a/SourceCode/Bond/EAPSimulator/CHsmsActive.h b/SourceCode/Bond/EAPSimulator/CHsmsActive.h
index 0383681..f46f279 100644
--- a/SourceCode/Bond/EAPSimulator/CHsmsActive.h
+++ b/SourceCode/Bond/EAPSimulator/CHsmsActive.h
@@ -4,6 +4,7 @@
#include <map>
#include <set>
#include "CCollectionEvent.h"
+#include "ProcessJob.h"
#define SVID_CJobSpace 5001
@@ -84,6 +85,8 @@
const char* pszCarrierId,
unsigned char PTN);
+ // S16F15
+ int hsmsPRJobMultiCreate(std::vector<SERVO::CProcessJob*>& pjs);
// 通过的reply函数
void replyAck(int s, int f, unsigned int systemBytes, BYTE ack, const char* pszAckName);
diff --git a/SourceCode/Bond/EAPSimulator/CPJsDlg.cpp b/SourceCode/Bond/EAPSimulator/CPJsDlg.cpp
new file mode 100644
index 0000000..212291f
--- /dev/null
+++ b/SourceCode/Bond/EAPSimulator/CPJsDlg.cpp
@@ -0,0 +1,161 @@
+锘�// CPJsDlg.cpp: 瀹炵幇鏂囦欢
+//
+
+#include "pch.h"
+#include "EAPSimulator.h"
+#include "CPJsDlg.h"
+#include "afxdialogex.h"
+
+
+// CPJsDlg 瀵硅瘽妗�
+
+IMPLEMENT_DYNAMIC(CPJsDlg, CDialogEx)
+
+CPJsDlg::CPJsDlg(CWnd* pParent /*=nullptr*/)
+ : CDialogEx(IDD_DIALOG_PJS, pParent)
+{
+
+}
+
+CPJsDlg::~CPJsDlg()
+{
+ for (auto item : m_pjs) {
+ delete item;
+ }
+ m_pjs.clear();
+}
+
+void CPJsDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialogEx::DoDataExchange(pDX);
+}
+
+
+BEGIN_MESSAGE_MAP(CPJsDlg, CDialogEx)
+ ON_BN_CLICKED(IDC_BUTTON_ADD, &CPJsDlg::OnBnClickedButtonAdd)
+ ON_BN_CLICKED(IDC_BUTTON_DELETE, &CPJsDlg::OnBnClickedButtonDelete)
+ ON_BN_CLICKED(IDC_BUTTON_SEND, &CPJsDlg::OnBnClickedButtonSend)
+END_MESSAGE_MAP()
+
+
+// CPJsDlg 娑堟伅澶勭悊绋嬪簭
+
+
+void CPJsDlg::OnBnClickedButtonAdd()
+{
+ // TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
+}
+
+void CPJsDlg::OnBnClickedButtonDelete()
+{
+ CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST1);
+ int nSel = pListCtrl->GetNextItem(-1, LVNI_SELECTED);
+
+ if (nSel != -1) {
+ SERVO::CProcessJob* pj = (SERVO::CProcessJob*)pListCtrl->GetItemData(nSel);
+ for (auto iter = m_pjs.begin(); iter != m_pjs.end(); ++iter) {
+ if (*iter == pj) {
+ delete (*iter);
+ m_pjs.erase(iter);
+ break;
+ }
+ }
+ }
+
+ pListCtrl->DeleteItem(nSel);
+}
+
+void CPJsDlg::OnBnClickedButtonSend()
+{
+ theApp.m_model.m_pHsmsActive->hsmsPRJobMultiCreate(m_pjs);
+}
+
+BOOL CPJsDlg::OnInitDialog()
+{
+ CDialogEx::OnInitDialog();
+
+
+ // 鎶ヨ〃鎺т欢
+ CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST1);
+ DWORD dwStyle = pListCtrl->GetExtendedStyle();
+ dwStyle |= LVS_EX_FULLROWSELECT;
+ dwStyle |= LVS_EX_GRIDLINES;
+ pListCtrl->SetExtendedStyle(dwStyle);
+
+ HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1);
+ ListView_SetImageList(pListCtrl->GetSafeHwnd(), imageList, LVSIL_SMALL);
+ pListCtrl->InsertColumn(0, _T(""), LVCFMT_RIGHT, 0);
+ pListCtrl->InsertColumn(1, _T("PJ ID"), LVCFMT_LEFT, 100);
+ pListCtrl->InsertColumn(2, _T("Carrier1 & Slots"), LVCFMT_LEFT, 180);
+ pListCtrl->InsertColumn(3, _T("Carrier2 & Slots"), LVCFMT_LEFT, 180);
+ pListCtrl->InsertColumn(4, _T("Carrier3 & Slots"), LVCFMT_LEFT, 180);
+ pListCtrl->InsertColumn(5, _T("Carrier4 & Slots"), LVCFMT_LEFT, 180);
+ pListCtrl->InsertColumn(6, _T("PPID"), LVCFMT_LEFT, 180);
+
+
+
+ // 鍒涘缓鐢ㄤ簬娴嬭瘯鐨凱J
+ {
+ SERVO::CProcessJob* pj = new SERVO::CProcessJob("PJ0001");
+ std::vector<uint8_t> slots1{ 1, 2, 3 };
+ pj->addCarrier("CID1001", slots1);
+ pj->setRecipe(SERVO::RecipeMethod::NoTuning, "P1001");
+ m_pjs.push_back(pj);
+ }
+ {
+ SERVO::CProcessJob* pj = new SERVO::CProcessJob("PJ0002");
+ std::vector<uint8_t> slots1{ 1, 3 };
+ pj->addCarrier("CID1002", slots1);
+ std::vector<uint8_t> slots2{ 1};
+ pj->addCarrier("CID1003", slots2);
+ pj->setRecipe(SERVO::RecipeMethod::NoTuning, "R002");
+ m_pjs.push_back(pj);
+ }
+ {
+ SERVO::CProcessJob* pj = new SERVO::CProcessJob("PJ0003");
+ std::vector<uint8_t> slots1{ 1, 2, 3, 5 };
+ pj->addCarrier("CID1004", slots1);
+ pj->setRecipe(SERVO::RecipeMethod::NoTuning, "P1001");
+ m_pjs.push_back(pj);
+ }
+
+ // 鏄剧ず鍒版姤琛ㄤ腑
+ for (auto item : m_pjs) {
+ AddPjToListCtrl(item);
+ }
+
+
+ return TRUE; // return TRUE unless you set the focus to a control
+ // 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
+}
+
+void CPJsDlg::AddPjToListCtrl(SERVO::CProcessJob* pj)
+{
+ CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST1);
+ pListCtrl->InsertItem(0, _T(""));
+ pListCtrl->SetItemData(0, (DWORD_PTR)pj);
+ pListCtrl->SetItemText(0, 1, pj->id().c_str());
+ pListCtrl->SetItemText(0, 6, pj->recipeSpec().c_str());
+
+ auto carries = pj->carriers();
+ for (int i = 0; i < min(4, carries.size()); i++) {
+ pListCtrl->SetItemText(0, 2 + i, GetFormatString(carries[i])); ;
+ }
+}
+
+CString CPJsDlg::GetFormatString(SERVO::CarrierSlotInfo& csi)
+{
+ CString strRet;
+ strRet.Append(csi.carrierId.c_str());
+ strRet.Append("<");
+ int size = min(8, csi.slots.size());
+ for (int i = 0; i < size; i++) {
+ strRet.Append(std::to_string(csi.slots[i]).c_str());
+ if (i != size - 1) {
+ strRet.Append(",");
+ }
+ }
+ strRet.Append(">");
+
+ return strRet;
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/EAPSimulator/CPJsDlg.h b/SourceCode/Bond/EAPSimulator/CPJsDlg.h
new file mode 100644
index 0000000..2441c8b
--- /dev/null
+++ b/SourceCode/Bond/EAPSimulator/CPJsDlg.h
@@ -0,0 +1,35 @@
+锘�#pragma once
+#include "ProcessJob.h"
+#include <vector>
+
+
+// CPJsDlg 瀵硅瘽妗�
+
+class CPJsDlg : public CDialogEx
+{
+ DECLARE_DYNAMIC(CPJsDlg)
+
+public:
+ CPJsDlg(CWnd* pParent = nullptr); // 鏍囧噯鏋勯�犲嚱鏁�
+ virtual ~CPJsDlg();
+ void AddPjToListCtrl(SERVO::CProcessJob* pj);
+ CString GetFormatString(SERVO::CarrierSlotInfo& csi);
+
+private:
+ std::vector<SERVO::CProcessJob*> m_pjs;
+
+// 瀵硅瘽妗嗘暟鎹�
+#ifdef AFX_DESIGN_TIME
+ enum { IDD = IDD_DIALOG_PJS };
+#endif
+
+protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 鏀寔
+
+ DECLARE_MESSAGE_MAP()
+public:
+ afx_msg void OnBnClickedButtonAdd();
+ afx_msg void OnBnClickedButtonDelete();
+ afx_msg void OnBnClickedButtonSend();
+ virtual BOOL OnInitDialog();
+};
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulator.rc b/SourceCode/Bond/EAPSimulator/EAPSimulator.rc
index 53f3fad..245a5d1 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulator.rc
+++ b/SourceCode/Bond/EAPSimulator/EAPSimulator.rc
Binary files differ
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulator.vcxproj b/SourceCode/Bond/EAPSimulator/EAPSimulator.vcxproj
index a614abf..868c15c 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulator.vcxproj
+++ b/SourceCode/Bond/EAPSimulator/EAPSimulator.vcxproj
@@ -93,6 +93,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_WINDOWS;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ <LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -193,6 +194,7 @@
<ClInclude Include="CModel.h" />
<ClInclude Include="Common.h" />
<ClInclude Include="Context.h" />
+ <ClInclude Include="CPJsDlg.h" />
<ClInclude Include="CReport.h" />
<ClInclude Include="CTerminalDisplayDlg.h" />
<ClInclude Include="CVariable.h" />
@@ -202,6 +204,7 @@
<ClInclude Include="Log.h" />
<ClInclude Include="LogEdit.h" />
<ClInclude Include="pch.h" />
+ <ClInclude Include="ProcessJob.h" />
<ClInclude Include="Resource.h" />
<ClInclude Include="targetver.h" />
</ItemGroup>
@@ -215,6 +218,7 @@
<ClCompile Include="CLinkReportDlg.cpp" />
<ClCompile Include="CModel.cpp" />
<ClCompile Include="Context.cpp" />
+ <ClCompile Include="CPJsDlg.cpp" />
<ClCompile Include="CReport.cpp" />
<ClCompile Include="CTerminalDisplayDlg.cpp" />
<ClCompile Include="CVariable.cpp" />
@@ -228,6 +232,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="ProcessJob.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="EAPSimulator.rc" />
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulator.vcxproj.filters b/SourceCode/Bond/EAPSimulator/EAPSimulator.vcxproj.filters
index dab50ba..d100ea3 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulator.vcxproj.filters
+++ b/SourceCode/Bond/EAPSimulator/EAPSimulator.vcxproj.filters
@@ -78,6 +78,12 @@
<ClInclude Include="CLinkReportDetailDlg.h">
<Filter>澶存枃浠�</Filter>
</ClInclude>
+ <ClInclude Include="CPJsDlg.h">
+ <Filter>澶存枃浠�</Filter>
+ </ClInclude>
+ <ClInclude Include="ProcessJob.h">
+ <Filter>澶存枃浠�</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="EAPSimulator.cpp">
@@ -131,6 +137,12 @@
<ClCompile Include="CLinkReportDetailDlg.cpp">
<Filter>婧愭枃浠�</Filter>
</ClCompile>
+ <ClCompile Include="CPJsDlg.cpp">
+ <Filter>婧愭枃浠�</Filter>
+ </ClCompile>
+ <ClCompile Include="ProcessJob.cpp">
+ <Filter>婧愭枃浠�</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="EAPSimulator.rc">
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp b/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp
index eaed5c6..9ced073 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp
+++ b/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp
@@ -13,6 +13,7 @@
#include "CEDEventReportDlg.h"
#include "CDefineReportsDlg.h"
#include "CLinkReportDlg.h"
+#include "CPJsDlg.h"
#ifdef _DEBUG
@@ -92,6 +93,7 @@
ON_BN_CLICKED(IDC_BUTTON_CARRIER_RELEASE, &CEAPSimulatorDlg::OnBnClickedButtonCarrierRelease)
ON_BN_CLICKED(IDC_BUTTON_QUERY_CJ_SPACE, &CEAPSimulatorDlg::OnBnClickedButtonQueryCjSpace)
ON_BN_CLICKED(IDC_BUTTON_QUERY_PJ_SPACE, &CEAPSimulatorDlg::OnBnClickedButtonQueryPjSpace)
+ ON_BN_CLICKED(IDC_BUTTON_CREATE_PJ, &CEAPSimulatorDlg::OnBnClickedButtonCreatePj)
END_MESSAGE_MAP()
@@ -283,6 +285,7 @@
GetDlgItem(IDC_BUTTON_CARRIER_RELEASE)->EnableWindow(enabled);
GetDlgItem(IDC_BUTTON_QUERY_CJ_SPACE)->EnableWindow(enabled);
GetDlgItem(IDC_BUTTON_QUERY_PJ_SPACE)->EnableWindow(enabled);
+ GetDlgItem(IDC_BUTTON_CREATE_PJ)->EnableWindow(enabled);
}
void CEAPSimulatorDlg::OnBnClickedButtonConnect()
@@ -405,3 +408,9 @@
{
theApp.m_model.m_pHsmsActive->hsmsCarrierRelease(DATAID++, "CSX 52078", 2);
}
+
+void CEAPSimulatorDlg::OnBnClickedButtonCreatePj()
+{
+ CPJsDlg dlg;
+ dlg.DoModal();
+}
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h b/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h
index 42e69d2..e289190 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h
+++ b/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h
@@ -61,4 +61,5 @@
afx_msg void OnBnClickedButtonCarrierRelease();
afx_msg void OnBnClickedButtonQueryCjSpace();
afx_msg void OnBnClickedButtonQueryPjSpace();
+ afx_msg void OnBnClickedButtonCreatePj();
};
diff --git a/SourceCode/Bond/EAPSimulator/ProcessJob.cpp b/SourceCode/Bond/EAPSimulator/ProcessJob.cpp
new file mode 100644
index 0000000..a4935e6
--- /dev/null
+++ b/SourceCode/Bond/EAPSimulator/ProcessJob.cpp
@@ -0,0 +1,252 @@
+#include "pch.h"
+#include "ProcessJob.h"
+#include <cctype>
+
+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(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());
+ }
+
+ std::vector<CProcessJob::ValidationIssue>
+ CProcessJob::validate(const IResourceView& rv) const
+ {
+ std::vector<ValidationIssue> issues;
+
+ // 让 add 同时支持 const char* 和 std::string
+ auto add = [&](uint32_t code, std::string msg) {
+ issues.push_back({ code, std::move(msg) });
+ };
+
+ // —— 基本 / 标识 ——
+ 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 issues;
+ }
+
+ // —— 状态机 ——
+ // 规则可按你们协议微调
+ 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));
+ }
+ }
+}
diff --git a/SourceCode/Bond/EAPSimulator/ProcessJob.h b/SourceCode/Bond/EAPSimulator/ProcessJob.h
new file mode 100644
index 0000000..92d70b9
--- /dev/null
+++ b/SourceCode/Bond/EAPSimulator/ProcessJob.h
@@ -0,0 +1,198 @@
+#pragma once
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+#include <algorithm>
+#include <cstdint>
+#include <chrono>
+#include <optional>
+
+namespace SERVO {
+ /// PJ 生命周期(贴近 E40 常见状态)
+ enum class PJState : uint8_t {
+ NoState = 0,
+ Queued,
+ SettingUp,
+ InProcess,
+ Paused,
+ Aborting,
+ Completed,
+ Aborted,
+ Failed
+ };
+
+ /// 配方指定方式(对应 S16F15 里 PRRECIPEMETHOD)
+ enum class RecipeMethod : uint8_t {
+ NoTuning = 1, // 1 - recipe without variable tuning
+ WithTuning = 2 // 2 - recipe with variable tuning
+ };
+
+ /// 启动策略(对应 S16F15 里 PRPROCESSSTART)
+ enum class StartPolicy : uint8_t {
+ Queued = 0, // 建立后排队
+ AutoStart = 1 // 条件满足则自动启动
+ };
+
+ /** 配方参数对(S16F15 中 RCPPARNM / RCPPARVAL) */
+ struct PJParam {
+ std::string name; // RCPPARNM
+ std::string value; // RCPPARVAL
+ };
+
+ /**
+ {L:2
+ CARRIERID
+ {L:j
+ SLOTID
+ }
+ }
+ */
+ struct CarrierSlotInfo {
+ std::string carrierId; // CARRIERID
+ std::vector<uint8_t> slots; // SLOTID[]
+ };
+
+ /// 简单资源视图接口:供 Validate() 查询(由设备端实现者在外部提供)
+ struct IResourceView {
+ virtual ~IResourceView() = default;
+ virtual bool recipeExists(const std::string& ppid) const = 0;
+ virtual bool carrierPresent(const std::string& carrierId) const = 0;
+ virtual bool slotUsable(const std::string& carrierId, uint16_t slot) const = 0;
+ virtual bool ceidDefined(uint32_t ceid) const = 0;
+ // 你也可以扩展:port状态、占用情况、CJ/PJ空间等
+ };
+
+ /// PJ 主类
+ /**
+ * ProcessJob —— 与 S16F15(PRJobMultiCreate)字段一一对应的承载类
+ *
+ * S16F15 结构(核心节选):
+ * {L:6
+ * PRJOBID -> m_pjId
+ * MF -> m_mf
+ * {L:n { CARRIERID {L:j SLOTID} } }
+ * {L:3
+ * PRRECIPEMETHOD -> m_recipeType
+ * RCPSPEC(PPID) -> m_recipeSpec
+ * {L:m { RCPPARNM RCPPARVAL }} -> m_params
+ * }
+ * PRPROCESSSTART -> m_startPolicy
+ * {L:k PRPAUSEEVENTID} -> m_pauseEvents
+ * }
+ */
+ class CProcessJob {
+ public:
+ // —— 构造 / 基本设置 ——
+ explicit CProcessJob(std::string pjId);
+
+ const std::string& id() const noexcept { return m_pjId; }
+ const std::string& parentCjId() const noexcept { return m_parentCjId; }
+ PJState state() const noexcept { return m_state; }
+ StartPolicy startPolicy() const noexcept { return m_startPolicy; }
+ RecipeMethod recipeMethod() const noexcept { return m_recipeMethod; }
+ const std::string& recipeSpec() const noexcept { return m_recipeSpec; } // PPID 或 Spec
+
+ // 绑定父 CJ
+ void setParentCjId(std::string cjId);
+
+ // 配方
+ void setRecipe(RecipeMethod method, std::string spec);
+
+ // 启动策略
+ void setStartPolicy(StartPolicy sp) { m_startPolicy = sp; }
+
+ // 参数
+ void addParam(std::string name, std::string value);
+ void setParams(std::vector<PJParam> params);
+
+ // 暂停事件
+ void addPauseEvent(uint32_t ceid);
+ void setPauseEvents(std::vector<uint32_t> ceids);
+
+ // —— 校验 ——
+ struct ValidationIssue {
+ uint32_t code; // 自定义错误码
+ std::string text; // 文本描述
+ };
+ // 返回问题清单(空=通过)
+ std::vector<ValidationIssue> validate(const IResourceView& rv) const;
+
+ // —— 状态机(带守卫)——
+ bool queue(); // NoState -> Queued
+ bool start(); // Queued/SettingUp -> InProcess
+ bool enterSettingUp(); // Queued -> SettingUp
+ bool pause(); // InProcess -> Paused
+ bool resume(); // Paused -> InProcess
+ bool complete(); // InProcess -> Completed
+ bool abort(); // Any (未终态) -> Aborted
+ bool fail(std::string reason); // 任意态 -> Failed(记录失败原因)
+
+ // —— 访问器(用于上报/查询)——
+ const std::vector<PJParam>& params() const noexcept { return m_params; }
+ const std::vector<uint32_t>& pauseEvents() const noexcept { return m_pauseEvents; }
+ const std::string& failReason() const noexcept { return m_failReason; }
+
+ // 时间戳(可用于报表/追溯)
+ std::optional<std::chrono::system_clock::time_point> tQueued() const { return m_tQueued; }
+ std::optional<std::chrono::system_clock::time_point> tStart() const { return m_tStart; }
+ std::optional<std::chrono::system_clock::time_point> tEnd() const { return m_tEnd; }
+
+ // 长度限制工具(可在集成时统一策略)
+ static void clampString(std::string& s, size_t maxLen);
+ static bool asciiPrintable(const std::string& s);
+
+ // 清空并整体设置
+ void setCarriers(std::vector<CarrierSlotInfo> carriers);
+
+ // 追加一个载具
+ void addCarrier(std::string carrierId, std::vector<uint8_t> slots);
+
+ // 访问器
+ const std::vector<CarrierSlotInfo>& carriers() const noexcept { return m_carriers; }
+
+ // 判定是否“按载具/卡位”方式
+ bool usesCarrierSlots() const noexcept { return !m_carriers.empty(); }
+
+
+ private:
+ // 内部状态转移帮助
+ void markQueued();
+ void markStart();
+ void markEnd();
+
+ private:
+ // 标识
+ std::string m_pjId;
+ std::string m_parentCjId;
+
+ // 配方
+ RecipeMethod m_recipeMethod{ RecipeMethod::NoTuning };
+ std::string m_recipeSpec; // PPID / Spec
+
+ // 物料
+ static constexpr uint8_t MATERIAL_FORMAT = 14; // substrate
+ std::vector<CarrierSlotInfo> m_carriers; // {L:n { CARRIERID {L:j SLOTID} }}
+
+ // 参数 / 暂停事件
+ std::vector<PJParam> m_params;
+ std::vector<uint32_t> m_pauseEvents;
+
+ // 状态 & 记录
+ StartPolicy m_startPolicy{ StartPolicy::Queued }; // 0=Queued, 1=AutoStart
+ PJState m_state{ PJState::NoState };
+ std::string m_failReason;
+
+ // 时间戳
+ std::optional<std::chrono::system_clock::time_point> m_tQueued;
+ std::optional<std::chrono::system_clock::time_point> m_tStart;
+ std::optional<std::chrono::system_clock::time_point> m_tEnd;
+
+ // 约束(可按你们协议调整)
+ static constexpr size_t MAX_ID_LEN = 64; // PJID/ CJID/ CarrierID/ MID/ PPID
+ static constexpr size_t MAX_PARAM_K = 32; // 参数名
+ static constexpr size_t MAX_PARAM_V = 64; // 参数值
+ };
+}
+
+
diff --git a/SourceCode/Bond/EAPSimulator/Resource.h b/SourceCode/Bond/EAPSimulator/Resource.h
index a16bb26..6f261c8 100644
--- a/SourceCode/Bond/EAPSimulator/Resource.h
+++ b/SourceCode/Bond/EAPSimulator/Resource.h
@@ -13,6 +13,7 @@
#define IDD_DIALOG_ADD_IDS 135
#define IDD_DIALOG_LINK_REPORT 137
#define IDD_DIALOG_LINK_REPORT_DETAIL 139
+#define IDD_DIALOG_PJS 141
#define IDC_EDIT_LOG 1000
#define IDC_EDIT_IP 1001
#define IDC_EDIT_PORT 1002
@@ -51,14 +52,17 @@
#define IDC_BUTTON_CARRIER_RELEASE 1036
#define IDC_BUTTON_QUERY_CJ_SPACE 1037
#define IDC_BUTTON_QUERY_PJ_SPACE 1038
+#define IDC_BUTTON_CREATE_PJ 1039
+#define IDC_BUTTON_ADD 1040
+#define IDC_BUTTON_DELETE 1041
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 141
+#define _APS_NEXT_RESOURCE_VALUE 143
#define _APS_NEXT_COMMAND_VALUE 32771
-#define _APS_NEXT_CONTROL_VALUE 1039
+#define _APS_NEXT_CONTROL_VALUE 1042
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
diff --git a/SourceCode/Bond/Servo/CLoadPort.cpp b/SourceCode/Bond/Servo/CLoadPort.cpp
index f603990..0050200 100644
--- a/SourceCode/Bond/Servo/CLoadPort.cpp
+++ b/SourceCode/Bond/Servo/CLoadPort.cpp
@@ -363,7 +363,6 @@
// 模拟测试
- /*
if (m_nIndex == 0) {
static int ii = 0;
ii++;
@@ -373,12 +372,25 @@
CPortStatusReport portStatusReport;
portStatusReport.setPortStatus(PORT_INUSE);
portStatusReport.setJobExistenceSlot(0xf);
- portStatusReport.setCassetteId("CID1984113");
+ portStatusReport.setCassetteId("CID1001");
int nRet = portStatusReport.serialize(szBuffer, 64);
decodePortStatusReport(pStep, szBuffer, 64);
}
}
- */
+ if (m_nIndex == 1) {
+ static int ii = 0;
+ ii++;
+ if (ii == 55) {
+ char szBuffer[64] = { 0 };
+ CStep* pStep = getStepWithName(STEP_EQ_PORT1_INUSE);
+ CPortStatusReport portStatusReport;
+ portStatusReport.setPortStatus(PORT_INUSE);
+ portStatusReport.setJobExistenceSlot(0xf);
+ portStatusReport.setCassetteId("CID1004");
+ int nRet = portStatusReport.serialize(szBuffer, 64);
+ decodePortStatusReport(pStep, szBuffer, 64);
+ }
+ }
}
void CLoadPort::serialize(CArchive& ar)
diff --git a/SourceCode/Bond/Servo/CMaster.cpp b/SourceCode/Bond/Servo/CMaster.cpp
index b0424eb..f664b6e 100644
--- a/SourceCode/Bond/Servo/CMaster.cpp
+++ b/SourceCode/Bond/Servo/CMaster.cpp
@@ -3,6 +3,7 @@
#include "CMaster.h"
#include <future>
#include <vector>
+#include "RecipeManager.h"
namespace SERVO {
@@ -62,6 +63,11 @@
CMaster::~CMaster()
{
+ for (auto item : m_processJobs) {
+ delete item;
+ }
+ m_processJobs.clear();
+
if (m_hEventReadBitsThreadExit[0] != nullptr) {
::CloseHandle(m_hEventReadBitsThreadExit[0]);
m_hEventReadBitsThreadExit[0] = nullptr;
@@ -1154,6 +1160,15 @@
return nullptr;
}
+ CEquipment* CMaster::getEquipment(int id) const
+ {
+ for (auto item : m_listEquipment) {
+ if (item->getID() == id) return item;
+ }
+
+ return nullptr;
+ }
+
/*
* 添加LoadPort1
* index -- 0~3
@@ -1831,4 +1846,63 @@
{
m_nContinuousTransferCount = round;
}
+
+ int CMaster::setProcessJobs(std::vector<SERVO::CProcessJob*>& pjs)
+ {
+ std::vector<SERVO::CProcessJob*> temp;
+ for (auto p : pjs) {
+ if (p->validate(*this)) {
+ temp.push_back(p);
+ }
+ }
+
+ m_processJobs = temp;
+ return m_processJobs.size();
+ }
+
+ CLoadPort* CMaster::getPortWithCarrierId(const std::string& carrierId) const
+ {
+ CLoadPort* pPort;
+ int eqid[] = { EQ_ID_LOADPORT1, EQ_ID_LOADPORT2, EQ_ID_LOADPORT3, EQ_ID_LOADPORT4};
+ for (int i = 0; i < 4; i++) {
+ pPort = (CLoadPort*)getEquipment(eqid[i]);
+ ASSERT(pPort);
+ if (pPort->getCassetteId().compare(carrierId) == 0) return pPort;
+ }
+
+ return nullptr;
+ }
+
+ bool CMaster::isProcessJobsEmpty() const
+ {
+ return m_processJobs.empty();
+ }
+
+ bool CMaster::recipeExists(const std::string& ppid) const
+ {
+ std::vector<std::string> vecRecipe = RecipeManager::getInstance().getAllPPID();
+ bool exists = std::find(vecRecipe.begin(), vecRecipe.end(), ppid) != vecRecipe.end();
+ return exists;
+ }
+
+ bool CMaster::carrierPresent(const std::string& carrierId) const
+ {
+ CLoadPort* pPort = getPortWithCarrierId(carrierId);
+ return pPort != nullptr;
+ }
+
+ bool CMaster::slotUsable(const std::string& carrierId, uint16_t slot) const
+ {
+ CLoadPort* pPort = getPortWithCarrierId(carrierId);
+ if(pPort == nullptr) return false;
+ CSlot* pSlot = pPort->getSlot(slot);
+ if (pSlot == nullptr) return false;
+ return pSlot->isEnable();
+ }
+
+ bool CMaster::ceidDefined(uint32_t ceid) const
+ {
+ return true;
+ }
+
}
diff --git a/SourceCode/Bond/Servo/CMaster.h b/SourceCode/Bond/Servo/CMaster.h
index cf687de..f19f047 100644
--- a/SourceCode/Bond/Servo/CMaster.h
+++ b/SourceCode/Bond/Servo/CMaster.h
@@ -13,7 +13,7 @@
#include "CArmTray.h"
#include "CCLinkIEControl.h"
#include "CRobotTask.h"
-
+#include "ProcessJob.h"
#define CTStep_Unknow 0
@@ -64,7 +64,7 @@
ONCTROUNDEND onCTRoundEnd;
} MasterListener;
- class CMaster
+ class CMaster : public IResourceView
{
public:
CMaster();
@@ -87,6 +87,7 @@
void onTimer(UINT nTimerid);
std::list<CEquipment*>& getEquipmentList();
CEquipment* getEquipment(int id);
+ CEquipment* getEquipment(int id) const;
void setCacheFilepath(const char* pszFilepath);
int abortCurrentTask();
int restoreCurrentTask();
@@ -104,6 +105,8 @@
int carrierRelease(unsigned int port);
int getContinuousTransferCount();
void setContinuousTransferCount(int round);
+ int setProcessJobs(std::vector<SERVO::CProcessJob*>& pjs);
+ CLoadPort* getPortWithCarrierId(const std::string& carrierId) const;
private:
inline void lock() { EnterCriticalSection(&m_criticalSection); }
@@ -134,6 +137,14 @@
CRobotTask* createTransferTask_restore(CEquipment* pEqSrc, CLoadPort** pPorts);
CRobotTask* createTransferTask_continuous_transfer(CEquipment* pSrcEq, int nSrcSlot,
CEquipment* pTarEq, int nTarSlot, int armNo = 1);
+
+ public:
+ // —— IResourceView 覆写 ——(注意 const)
+ bool isProcessJobsEmpty() const override;
+ bool recipeExists(const std::string& ppid) const override;
+ bool carrierPresent(const std::string& carrierId) const override;
+ bool slotUsable(const std::string& carrierId, uint16_t slot) const override;
+ bool ceidDefined(uint32_t ceid) const override;
private:
CRITICAL_SECTION m_criticalSection;
@@ -179,6 +190,7 @@
private:
bool m_bEnableEventReport;
bool m_bEnableAlarmReport;
+ std::vector<SERVO::CProcessJob*> m_processJobs;
};
}
diff --git a/SourceCode/Bond/Servo/HsmsPassive.cpp b/SourceCode/Bond/Servo/HsmsPassive.cpp
index 1a18950..f44acb0 100644
--- a/SourceCode/Bond/Servo/HsmsPassive.cpp
+++ b/SourceCode/Bond/Servo/HsmsPassive.cpp
@@ -612,6 +612,9 @@
else if (nStream == 10 && pHeader->function == 3) {
replyTerminalDisplay(pMessage);
}
+ else if (nStream == 16 && pHeader->function == 15) {
+ replyPRJobMultiCreate(pMessage);
+ }
};
PassiveListener listener;
@@ -1514,6 +1517,111 @@
return 0;
}
+// S16F15
+int CHsmsPassive::replyPRJobMultiCreate(IMessage* pRecv)
+{
+ if (m_pPassive == NULL || STATE::SELECTED != m_pPassive->getState()) {
+ return ER_NOTSELECT;
+ }
+ ISECS2Item* pBody = pRecv->getBody();
+ if (pBody == nullptr || pBody->getType() != SITYPE::L) ER_PARAM_ERROR;
+
+
+ // 瑙i噴鏁版嵁锛屽緱鍒癈ProcessJob
+ ISECS2Item* pItemPjs, * pItemPj,* pItemCarriers, * pItemCarrier, *pItemSlots, *pItemRecipes;
+ unsigned int DATAID;
+ const char* pszPrjobid, *pszMF, *pszCarrierId, *pszRecipeName;
+ std::string strCarrierId;
+ unsigned int len;
+ unsigned char slot, PRRECIPEMETHOD;
+ std::vector<unsigned char> slots;
+ std::vector<SERVO::CProcessJob*> pjs;
+
+ if (!pBody->getSubItemU4(0, DATAID)) return ER_PARAM_ERROR;
+ pItemPjs = pBody->getSubItem(1);
+ if (pItemPjs == nullptr) return ER_PARAM_ERROR;
+ for (int i = 0; i < pItemPjs->getSubItemSize(); i++) {
+ pItemPj = pItemPjs->getSubItem(i);
+ if (pItemPj == nullptr) continue;
+ if (!pItemPj->getSubItemString(0, pszPrjobid)) continue;
+ if (!pItemPj->getSubItemBinary(1, pszMF, len)) continue;
+ pItemCarriers = pItemPj->getSubItem(2);
+ if (pItemCarriers == nullptr) continue;
+ pItemRecipes = pItemPj->getSubItem(3);
+ if (pItemRecipes == nullptr) continue;
+ SERVO::CProcessJob* pj = new SERVO::CProcessJob(pszPrjobid);
+ int size = pItemCarriers->getSubItemSize();
+ for (int j = 0; j < size; j++) {
+ pItemCarrier = pItemCarriers->getSubItem(j);
+ strCarrierId.clear();
+ if (pItemCarrier->getSubItemString(0, pszCarrierId)) {
+ strCarrierId = pszCarrierId;
+ }
+
+ slots.clear();
+ pItemSlots = pItemCarrier->getSubItem(1);
+ if (pItemSlots != nullptr) {
+ int size2 = pItemSlots->getSubItemSize();
+ for (int k = 0; k < size2; k++) {
+ if (pItemSlots->getSubItemU1(k, slot)) {
+ slots.push_back(slot);
+ }
+ }
+ }
+ pj->addCarrier(strCarrierId, slots);
+ }
+ if (pItemRecipes->getSubItemU1(0, PRRECIPEMETHOD)
+ && pItemRecipes->getSubItemString(1, pszRecipeName)) {
+ pj->setRecipe(SERVO::RecipeMethod(PRRECIPEMETHOD), std::string(pszRecipeName));
+ }
+
+ pjs.push_back(pj);
+ }
+
+ ASSERT(m_listener.onPRJobMultiCreate != nullptr);
+ int nRet = m_listener.onPRJobMultiCreate(this, pjs);
+
+
+ // 鍥炲鎶ユ枃
+ IMessage* pMessage = NULL;
+ HSMS_Create1Message(pMessage, m_nSessionId, 16, 16, ++m_nSystemByte);
+ ASSERT(pMessage);
+ ISECS2Item* pItemPrjobIds = pMessage->getBody()->addItem();
+ ISECS2Item* pItemErrors = pMessage->getBody()->addItem();
+ bool bHasError = false;
+ for (auto p : pjs) {
+ if (p->issue().empty()) {
+ pItemPrjobIds->addItem(p->id().c_str(), "PRJOBID");
+ }
+ else {
+ bHasError = true;
+ }
+ }
+ if (bHasError) {
+ pItemErrors->addBoolItem(false, "ACKA");
+ ISECS2Item* pItemErrors2 = pItemErrors->addItem();
+ for (auto p : pjs) {
+ if (!p->issue().empty()) {
+ ISECS2Item* pItemErr = pItemErrors2->addItem();
+ pItemErr->addU4Item(p->issue()[0].code, "ERRCODE");
+ pItemErr->addItem(("<" + p->id() + ">" + p->issue()[0].text).c_str(), "ERRTEXT");
+ }
+ }
+ }
+ m_pPassive->sendMessage(pMessage);
+ LOGI("<HSMS>[SECS Msg SEND]S16F16 (SysByte=%u)", pMessage->getHeader()->systemBytes);
+ HSMS_Destroy1Message(pMessage);
+
+
+ // 閲婃斁鏈夐棶棰�(鏈坊鍔犲埌master)鐨勫唴瀛�
+ for (auto p : pjs) {
+ if(!p->issue().empty()) delete p;
+ }
+ pjs.clear();
+
+ return 0;
+}
+
// S5F1
int CHsmsPassive::requestAlarmReport(int ALCD, int ALID, const char* ALTX)
{
diff --git a/SourceCode/Bond/Servo/HsmsPassive.h b/SourceCode/Bond/Servo/HsmsPassive.h
index f50de9b..ba36e0d 100644
--- a/SourceCode/Bond/Servo/HsmsPassive.h
+++ b/SourceCode/Bond/Servo/HsmsPassive.h
@@ -7,6 +7,7 @@
#include <map>
#include <set>
#include "CCollectionEvent.h"
+#include "ProcessJob.h"
#define EQCONSTANT_VALUE_MAX 64
@@ -86,6 +87,7 @@
const char* pszCarrierId,
unsigned char PTN,
std::string& strErrorTxt)> CARRIERACTION;
+typedef std::function<int(void* pFrom, std::vector<SERVO::CProcessJob*>& pjs)> PRJOBMULTICREATE;
typedef struct _SECSListener
{
SECSEQOFFLINE onEQOffLine;
@@ -98,6 +100,7 @@
EDALARMREPORT onEnableDisableAlarmReport;
QUERYPPIDLIST onQueryPPIDList;
CARRIERACTION onCarrierAction;
+ PRJOBMULTICREATE onPRJobMultiCreate;
} SECSListener;
@@ -207,6 +210,7 @@
int replyPurgeSpooledData(IMessage* pRecv);
int replyQueryPPIDList(IMessage* pRecv);
int replyTerminalDisplay(IMessage* pRecv);
+ int replyPRJobMultiCreate(IMessage* pRecv);
private:
inline void Lock() { EnterCriticalSection(&m_criticalSection); }
diff --git a/SourceCode/Bond/Servo/Model.cpp b/SourceCode/Bond/Servo/Model.cpp
index a6299e5..260c4fc 100644
--- a/SourceCode/Bond/Servo/Model.cpp
+++ b/SourceCode/Bond/Servo/Model.cpp
@@ -175,6 +175,12 @@
return CAACK_5;
LOGI("<Model>onCarrierAction %d, %s, %d, %d", DATAID, pszCarrierAction, pszCarrierId, PTN);
};
+ listener.onPRJobMultiCreate = [&](void* pFrom, std::vector<SERVO::CProcessJob*>& pjs) -> int {
+ for (auto p : pjs) {
+ LOGI("<Model>onPRJobMultiCreate %s %s", p->id().c_str(), p->recipeSpec().c_str());
+ }
+ return m_master.setProcessJobs(pjs);
+ };
m_hsmsPassive.setListener(listener);
m_hsmsPassive.setEquipmentModelType((LPTSTR)(LPCTSTR)strModeType);
m_hsmsPassive.setSoftRev((LPTSTR)(LPCTSTR)strSoftRev);
diff --git a/SourceCode/Bond/Servo/ProcessJob.cpp b/SourceCode/Bond/Servo/ProcessJob.cpp
new file mode 100644
index 0000000..2ca265a
--- /dev/null
+++ b/SourceCode/Bond/Servo/ProcessJob.cpp
@@ -0,0 +1,260 @@
+#include "stdafx.h"
+#include "ProcessJob.h"
+#include <cctype>
+
+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(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::issue()
+ {
+ 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));
+ }
+ }
+}
diff --git a/SourceCode/Bond/Servo/ProcessJob.h b/SourceCode/Bond/Servo/ProcessJob.h
new file mode 100644
index 0000000..9cf2fc5
--- /dev/null
+++ b/SourceCode/Bond/Servo/ProcessJob.h
@@ -0,0 +1,203 @@
+#pragma once
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+#include <algorithm>
+#include <cstdint>
+#include <chrono>
+#include <optional>
+
+namespace SERVO {
+ /// PJ 生命周期(贴近 E40 常见状态)
+ enum class PJState : uint8_t {
+ NoState = 0,
+ Queued,
+ SettingUp,
+ InProcess,
+ Paused,
+ Aborting,
+ Completed,
+ Aborted,
+ Failed
+ };
+
+ /// 配方指定方式(对应 S16F15 里 PRRECIPEMETHOD)
+ enum class RecipeMethod : uint8_t {
+ NoTuning = 1, // 1 - recipe without variable tuning
+ WithTuning = 2 // 2 - recipe with variable tuning
+ };
+
+ /// 启动策略(对应 S16F15 里 PRPROCESSSTART)
+ enum class StartPolicy : uint8_t {
+ Queued = 0, // 建立后排队
+ AutoStart = 1 // 条件满足则自动启动
+ };
+
+ /** 配方参数对(S16F15 中 RCPPARNM / RCPPARVAL) */
+ struct PJParam {
+ std::string name; // RCPPARNM
+ std::string value; // RCPPARVAL
+ };
+
+ /**
+ {L:2
+ CARRIERID
+ {L:j
+ SLOTID
+ }
+ }
+ */
+ struct CarrierSlotInfo {
+ std::string carrierId; // CARRIERID
+ std::vector<uint8_t> slots; // SLOTID[]
+ };
+
+ /// 简单资源视图接口:供 Validate() 查询(由设备端实现者在外部提供)
+ struct IResourceView {
+ virtual ~IResourceView() = default;
+ virtual bool isProcessJobsEmpty() const = 0;
+ virtual bool recipeExists(const std::string& ppid) const = 0;
+ virtual bool carrierPresent(const std::string& carrierId) const = 0;
+ virtual bool slotUsable(const std::string& carrierId, uint16_t slot) const = 0;
+ virtual bool ceidDefined(uint32_t ceid) const = 0;
+ // 你也可以扩展:port状态、占用情况、CJ/PJ空间等
+ };
+
+ /// PJ 主类
+ /**
+ * ProcessJob —— 与 S16F15(PRJobMultiCreate)字段一一对应的承载类
+ *
+ * S16F15 结构(核心节选):
+ * {L:6
+ * PRJOBID -> m_pjId
+ * MF -> m_mf
+ * {L:n { CARRIERID {L:j SLOTID} } }
+ * {L:3
+ * PRRECIPEMETHOD -> m_recipeType
+ * RCPSPEC(PPID) -> m_recipeSpec
+ * {L:m { RCPPARNM RCPPARVAL }} -> m_params
+ * }
+ * PRPROCESSSTART -> m_startPolicy
+ * {L:k PRPAUSEEVENTID} -> m_pauseEvents
+ * }
+ */
+ class CProcessJob {
+ public:
+ // —— 构造 / 基本设置 ——
+ explicit CProcessJob(std::string pjId);
+
+ const std::string& id() const noexcept { return m_pjId; }
+ const std::string& parentCjId() const noexcept { return m_parentCjId; }
+ PJState state() const noexcept { return m_state; }
+ StartPolicy startPolicy() const noexcept { return m_startPolicy; }
+ RecipeMethod recipeMethod() const noexcept { return m_recipeMethod; }
+ const std::string& recipeSpec() const noexcept { return m_recipeSpec; } // PPID 或 Spec
+
+ // 绑定父 CJ
+ void setParentCjId(std::string cjId);
+
+ // 配方
+ void setRecipe(RecipeMethod method, std::string spec);
+
+ // 启动策略
+ void setStartPolicy(StartPolicy sp) { m_startPolicy = sp; }
+
+ // 参数
+ void addParam(std::string name, std::string value);
+ void setParams(std::vector<PJParam> params);
+
+ // 暂停事件
+ void addPauseEvent(uint32_t ceid);
+ void setPauseEvents(std::vector<uint32_t> ceids);
+
+ // —— 校验 ——
+ struct ValidationIssue {
+ uint32_t code; // 自定义错误码
+ std::string text; // 文本描述
+ };
+ // 返回问题清单(空=通过)
+ bool validate(const IResourceView& rv);
+ const std::vector<ValidationIssue>& issue();
+
+ // —— 状态机(带守卫)——
+ bool queue(); // NoState -> Queued
+ bool start(); // Queued/SettingUp -> InProcess
+ bool enterSettingUp(); // Queued -> SettingUp
+ bool pause(); // InProcess -> Paused
+ bool resume(); // Paused -> InProcess
+ bool complete(); // InProcess -> Completed
+ bool abort(); // Any (未终态) -> Aborted
+ bool fail(std::string reason); // 任意态 -> Failed(记录失败原因)
+
+ // —— 访问器(用于上报/查询)——
+ const std::vector<PJParam>& params() const noexcept { return m_params; }
+ const std::vector<uint32_t>& pauseEvents() const noexcept { return m_pauseEvents; }
+ const std::string& failReason() const noexcept { return m_failReason; }
+
+ // 时间戳(可用于报表/追溯)
+ std::optional<std::chrono::system_clock::time_point> tQueued() const { return m_tQueued; }
+ std::optional<std::chrono::system_clock::time_point> tStart() const { return m_tStart; }
+ std::optional<std::chrono::system_clock::time_point> tEnd() const { return m_tEnd; }
+
+ // 长度限制工具(可在集成时统一策略)
+ static void clampString(std::string& s, size_t maxLen);
+ static bool asciiPrintable(const std::string& s);
+
+ // 清空并整体设置
+ void setCarriers(std::vector<CarrierSlotInfo> carriers);
+
+ // 追加一个载具
+ void addCarrier(std::string carrierId, std::vector<uint8_t> slots);
+
+ // 访问器
+ const std::vector<CarrierSlotInfo>& carriers() const noexcept { return m_carriers; }
+
+ // 判定是否“按载具/卡位”方式
+ bool usesCarrierSlots() const noexcept { return !m_carriers.empty(); }
+
+
+ private:
+ // 内部状态转移帮助
+ void markQueued();
+ void markStart();
+ void markEnd();
+
+ private:
+ // 标识
+ std::string m_pjId;
+ std::string m_parentCjId;
+
+ // 配方
+ RecipeMethod m_recipeMethod{ RecipeMethod::NoTuning };
+ std::string m_recipeSpec; // PPID / Spec
+
+ // 物料
+ static constexpr uint8_t MATERIAL_FORMAT = 14; // substrate
+ std::vector<CarrierSlotInfo> m_carriers; // {L:n { CARRIERID {L:j SLOTID} }}
+
+ // 参数 / 暂停事件
+ std::vector<PJParam> m_params;
+ std::vector<uint32_t> m_pauseEvents;
+
+ // 状态 & 记录
+ StartPolicy m_startPolicy{ StartPolicy::Queued }; // 0=Queued, 1=AutoStart
+ PJState m_state{ PJState::NoState };
+ std::string m_failReason;
+
+ // 时间戳
+ std::optional<std::chrono::system_clock::time_point> m_tQueued;
+ std::optional<std::chrono::system_clock::time_point> m_tStart;
+ std::optional<std::chrono::system_clock::time_point> m_tEnd;
+
+ // 约束(可按你们协议调整)
+ static constexpr size_t MAX_ID_LEN = 64; // PJID/ CJID/ CarrierID/ MID/ PPID
+ static constexpr size_t MAX_PARAM_K = 32; // 参数名
+ static constexpr size_t MAX_PARAM_V = 64; // 参数值
+
+ // 错误列表
+ std::vector<ValidationIssue> m_issues;
+ };
+}
+
+
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj b/SourceCode/Bond/Servo/Servo.vcxproj
index 008f416..8246915 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj
+++ b/SourceCode/Bond/Servo/Servo.vcxproj
@@ -117,6 +117,7 @@
<PreprocessorDefinitions>_WINDOWS;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>.;..;..\DatabaseSDK\include;..\MELSECSDK\include;.\CCLinkPerformance;.\GridControl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -324,6 +325,7 @@
<ClInclude Include="PageRobotCmd.h" />
<ClInclude Include="PageTransferLog.h" />
<ClInclude Include="PortConfigurationDlg.h" />
+ <ClInclude Include="ProcessJob.h" />
<ClInclude Include="ProductionLogManager.h" />
<ClInclude Include="RecipeDeviceBindDlg.h" />
<ClInclude Include="RecipeManager.h" />
@@ -470,6 +472,7 @@
<ClCompile Include="PageRobotCmd.cpp" />
<ClCompile Include="PageTransferLog.cpp" />
<ClCompile Include="PortConfigurationDlg.cpp" />
+ <ClCompile Include="ProcessJob.cpp" />
<ClCompile Include="ProductionLogManager.cpp" />
<ClCompile Include="RecipeDeviceBindDlg.cpp" />
<ClCompile Include="RecipeManager.cpp" />
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj.filters b/SourceCode/Bond/Servo/Servo.vcxproj.filters
index c82ddab..230f6df 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj.filters
+++ b/SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -176,6 +176,7 @@
<ClCompile Include="CPageVarialbles.cpp" />
<ClCompile Include="CPageReport.cpp" />
<ClCompile Include="CPageCollectionEvent.cpp" />
+ <ClCompile Include="ProcessJob.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="AlarmManager.h" />
@@ -357,6 +358,7 @@
<ClInclude Include="CPageVarialbles.h" />
<ClInclude Include="CPageReport.h" />
<ClInclude Include="CPageCollectionEvent.h" />
+ <ClInclude Include="ProcessJob.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Servo.rc" />
--
Gitblit v1.9.3