// CControlJobDlg.cpp: 实现文件
|
//
|
|
#include "stdafx.h"
|
#include "Servo.h"
|
#include "CControlJobDlg.h"
|
#include "afxdialogex.h"
|
|
// ===== 新增:标准库头 =====
|
#include <array>
|
#include <string>
|
#include <unordered_set>
|
#include <algorithm>
|
|
// ===== 新增:CString 的 Hash/Equal(跨 ANSI/Unicode 通吃)=====
|
struct CStringHash {
|
size_t operator()(const CString& s) const noexcept {
|
#ifdef _UNICODE
|
return std::hash<std::wstring>{}(std::wstring(s.GetString()));
|
#else
|
return std::hash<std::string>{}(std::string(s.GetString()));
|
#endif
|
}
|
};
|
struct CStringEqual {
|
bool operator()(const CString& a, const CString& b) const noexcept {
|
return a == b;
|
}
|
};
|
|
|
// CControlJobDlg 对话框
|
|
IMPLEMENT_DYNAMIC(CControlJobDlg, CDialogEx)
|
|
CControlJobDlg::CControlJobDlg(CWnd* pParent /*=nullptr*/)
|
: CDialogEx(IDD_DIALOG_CONTROL_JOB, pParent)
|
{
|
m_pControlJob = nullptr;
|
}
|
|
CControlJobDlg::~CControlJobDlg()
|
{
|
}
|
|
void CControlJobDlg::DoDataExchange(CDataExchange* pDX)
|
{
|
CDialogEx::DoDataExchange(pDX);
|
DDX_Control(pDX, IDC_LIST1, m_listCtrl);
|
}
|
|
|
BEGIN_MESSAGE_MAP(CControlJobDlg, CDialogEx)
|
ON_WM_SIZE()
|
ON_BN_CLICKED(IDC_BUTTON_COMPLETION_BATH, &CControlJobDlg::OnBnClickedButtonCompletionBath)
|
ON_BN_CLICKED(IDC_BUTTON_RELOAD, &CControlJobDlg::OnBnClickedButtonReload)
|
ON_WM_TIMER()
|
END_MESSAGE_MAP()
|
|
|
void CControlJobDlg::SetControlJob(SERVO::CControlJob* pControlJob)
|
{
|
m_pControlJob = pControlJob;
|
}
|
|
// CControlJobDlg 消息处理程序
|
BOOL CControlJobDlg::OnInitDialog()
|
{
|
CDialogEx::OnInitDialog();
|
|
|
// label字体
|
LOGFONT lf{};
|
GetFont()->GetLogFont(&lf);
|
lf.lfHeight = -20;
|
lf.lfWeight = FW_BOLD;
|
_tcscpy_s(lf.lfFaceName, _T("Arial"));
|
m_fontNoJob.CreateFontIndirect(&lf);
|
GetDlgItem(IDC_LABEL_NO_JOB)->SetFont(&m_fontNoJob);
|
|
|
// 列表控件
|
HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1);
|
ListView_SetImageList(m_listCtrl.GetSafeHwnd(), imageList, LVSIL_SMALL);
|
m_listCtrl.ModifyStyle(0, LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS);
|
m_listCtrl.InsertColumn(0, _T("ID"), LVCFMT_LEFT, 180);
|
m_listCtrl.InsertColumn(1, _T("类型"), LVCFMT_LEFT, 120);
|
m_listCtrl.InsertColumn(2, _T("状态"), LVCFMT_LEFT, 120);
|
m_listCtrl.InsertColumn(3, _T("配方"), LVCFMT_LEFT, 120);
|
m_listCtrl.InsertColumn(4, _T("Port / Carrier / Slot"), LVCFMT_LEFT, 180);
|
m_listCtrl.InsertColumn(5, _T("描述"), LVCFMT_LEFT, 220);
|
|
|
// 控件状态
|
Resize();
|
OnBnClickedButtonReload();
|
SetTimer(1, 2000, nullptr);
|
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
// 异常: OCX 属性页应返回 FALSE
|
}
|
|
void CControlJobDlg::OnSize(UINT nType, int cx, int cy)
|
{
|
CDialogEx::OnSize(nType, cx, cy);
|
if (GetDlgItem(IDC_LIST1) == nullptr) return;
|
Resize();
|
}
|
|
void CControlJobDlg::Resize()
|
{
|
CRect rcClient, rcItem;
|
CWnd* pItem;
|
|
GetClientRect(rcClient);
|
|
|
// 关闭按钮
|
int x2 = rcClient.right - 12;
|
int y = rcClient.bottom - 12;
|
pItem = GetDlgItem(IDCANCEL);
|
pItem->GetClientRect(&rcItem);
|
pItem->MoveWindow(x2 - rcItem.Width(),
|
y - rcItem.Height(),
|
rcItem.Width(), rcItem.Height());
|
x2 -= rcItem.Width();
|
x2 -= 8;
|
|
pItem = GetDlgItem(IDC_BUTTON_RELOAD);
|
pItem->GetClientRect(&rcItem);
|
pItem->MoveWindow(x2 - rcItem.Width(),
|
y - rcItem.Height(),
|
rcItem.Width(), rcItem.Height());
|
|
|
// 结批按钮
|
pItem = GetDlgItem(IDC_BUTTON_COMPLETION_BATH);
|
pItem->GetClientRect(&rcItem);
|
pItem->MoveWindow(12, y - rcItem.Height(),
|
rcItem.Width(), rcItem.Height());
|
y -= rcItem.Height();
|
y -= 12;
|
|
// 线
|
pItem = GetDlgItem(IDC_LINE1);
|
pItem->MoveWindow(12, y, rcClient.Width() - 24, 2);
|
y -= 2;
|
|
|
// Label
|
pItem = GetDlgItem(IDC_LABEL_NO_JOB);
|
pItem->GetClientRect(&rcItem);
|
pItem->MoveWindow((rcClient.Width() - rcItem.Width()) / 2,
|
(y - rcItem.Height()) / 2,
|
rcItem.Width(), rcItem.Height());
|
|
|
// ListCtrl
|
pItem = GetDlgItem(IDC_LIST1);
|
pItem->MoveWindow(12, 12, rcClient.Width() - 24, y - 12);
|
}
|
|
void CControlJobDlg::ShowGroup1(BOOL bShow)
|
{
|
GetDlgItem(IDC_LABEL_NO_JOB)->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
|
GetDlgItem(IDC_LINE1)->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
|
}
|
|
void CControlJobDlg::ShowGroup2(BOOL bShow)
|
{
|
GetDlgItem(IDC_LIST1)->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
|
}
|
|
void CControlJobDlg::LoadData(SERVO::CControlJob* pControlJob)
|
{
|
// —— 工具:按“第一列键”在 parent 下查找子节点
|
auto FindChildByKey = [&](CExpandableListCtrl::Node* parent, LPCTSTR key)->CExpandableListCtrl::Node* {
|
if (!parent) return nullptr;
|
for (auto& up : parent->children) {
|
auto* n = up.get();
|
if (n && n->cols.size() > 0 && n->cols[0].CompareNoCase(key) == 0)
|
return n;
|
}
|
return nullptr;
|
};
|
|
// —— 工具:有则更新、无则创建(6列)
|
auto EnsureChild = [&](CExpandableListCtrl::Node* parent, const std::array<CString, 6>& cols)->CExpandableListCtrl::Node* {
|
CExpandableListCtrl::Node* n = FindChildByKey(parent, cols[0]);
|
if (!n) {
|
n = m_listCtrl.InsertChild(parent, { cols[0], cols[1], cols[2], cols[3], cols[4], cols[5] });
|
}
|
else {
|
if ((int)n->cols.size() < 6) n->cols.resize(6);
|
for (int i = 0; i < 6; i++) if (n->cols[i] != cols[i]) n->cols[i] = cols[i];
|
}
|
return n;
|
};
|
|
// —— 工具:删除 parent 下“本次未出现”的子节点(基于第一列键)
|
auto RemoveStaleChildren = [&](CExpandableListCtrl::Node* parent, const std::unordered_set<CString, CStringHash, CStringEqual>& keep) {
|
if (!parent) return;
|
auto& vec = parent->children;
|
vec.erase(std::remove_if(vec.begin(), vec.end(), [&](const std::unique_ptr<CExpandableListCtrl::Node>& up) {
|
auto* n = up.get();
|
if (!n || n->cols.empty()) return true; // 防御:无效行直接删
|
return keep.find(n->cols[0]) == keep.end();
|
}), vec.end());
|
};
|
|
// ———————————— 1) 无数据:清空并复位缓存 ————————————
|
if (!pControlJob) {
|
m_listCtrl.ClearTree();
|
m_rootNode = nullptr;
|
m_lastCjPtr = nullptr;
|
m_lastCjId.Empty();
|
m_listCtrl.RebuildVisible();
|
return;
|
}
|
|
const CString curId = pControlJob->id().c_str();
|
const bool cjChanged = (pControlJob != m_lastCjPtr) || (curId != m_lastCjId);
|
|
// ———————————— 2) CJ 变了:整棵重建(保留展开标记不必管,重建即可) ————————————
|
if (cjChanged || !m_rootNode) {
|
m_listCtrl.ClearTree();
|
|
m_rootNode = m_listCtrl.InsertRoot({
|
pControlJob->id().c_str(), _T("ControlJob"),
|
pControlJob->getStateText().c_str(), _T(""), _T(""),
|
pControlJob->failReason().c_str()
|
});
|
|
auto pjs = pControlJob->getPjs();
|
for (auto pj : pjs) {
|
auto* pjNode = m_listCtrl.InsertChild(m_rootNode, {
|
pj->id().c_str(), _T("ProcessJob"),
|
pj->getStateText().c_str(), pj->recipeSpec().c_str(), _T(""),
|
pj->failReason().c_str()
|
});
|
|
auto cs = pj->carriers();
|
for (auto c : cs) {
|
for (auto g : c.contexts) {
|
auto* pGlass = static_cast<SERVO::CGlass*>(g);
|
if (pGlass) {
|
int port = 0, slot = 0;
|
pGlass->getOrginPort(port, slot);
|
CString carrier; carrier.Format(_T("%s / Port%d / Slot%d"),
|
CString(c.carrierId.c_str()), port + 1, slot + 1);
|
|
m_listCtrl.InsertChild(pjNode, {
|
pGlass->getID().c_str(), _T("Glass"),
|
pGlass->getStateText().c_str(), _T(""),
|
carrier, _T("")
|
});
|
}
|
else {
|
m_listCtrl.InsertChild(pjNode, {
|
_T("Null@") + CString(c.carrierId.c_str()), _T("Glass"),
|
_T(""), _T(""), CString(c.carrierId.c_str()), _T("")
|
});
|
}
|
}
|
}
|
pjNode->expanded = true;
|
}
|
m_rootNode->expanded = true;
|
|
m_lastCjPtr = pControlJob;
|
m_lastCjId = curId;
|
|
m_listCtrl.RebuildVisible();
|
return;
|
}
|
|
// ———————————— 3) CJ 未变:增量更新 ————————————
|
|
// 3.1 更新 CJ 行文本(状态可能变化)
|
if ((int)m_rootNode->cols.size() < 6) m_rootNode->cols.resize(6);
|
m_rootNode->cols[0] = pControlJob->id().c_str();
|
m_rootNode->cols[1] = _T("ControlJob");
|
m_rootNode->cols[2] = pControlJob->getStateText().c_str();
|
// cols[3] 保留为空
|
// cols[4] 保留为空
|
m_rootNode->cols[5] = pControlJob->failReason().c_str();
|
|
// 3.2 同步 PJ 层
|
std::unordered_set<CString, CStringHash, CStringEqual> pjKeysWanted;
|
|
auto pjs = pControlJob->getPjs();
|
for (auto pj : pjs) {
|
CString pjId = pj->id().c_str();
|
pjKeysWanted.insert(pjId);
|
|
auto* pjNode = FindChildByKey(m_rootNode, pjId);
|
if (!pjNode) {
|
pjNode = m_listCtrl.InsertChild(m_rootNode, {
|
pjId, _T("ProcessJob"),
|
pj->getStateText().c_str(), pj->recipeSpec().c_str(), _T(""),
|
pj->failReason().c_str()
|
});
|
pjNode->expanded = true; // 初次出现默认展开
|
}
|
else {
|
if ((int)pjNode->cols.size() < 6) pjNode->cols.resize(6);
|
pjNode->cols[1] = _T("ProcessJob");
|
pjNode->cols[2] = pj->getStateText().c_str();
|
pjNode->cols[3] = pj->recipeSpec().c_str();
|
pjNode->cols[5] = pj->failReason().c_str();
|
}
|
|
// 3.3 同步 Glass 层(第一列键:GlassID;空对象用 "Null@CarrierId" 防冲突)
|
std::unordered_set<CString, CStringHash, CStringEqual> gKeysWanted;
|
|
auto cs = pj->carriers();
|
for (auto c : cs) {
|
for (auto g : c.contexts) {
|
auto* pGlass = static_cast<SERVO::CGlass*>(g);
|
if (pGlass) {
|
int port = 0, slot = 0;
|
pGlass->getOrginPort(port, slot);
|
CString carrier; carrier.Format(_T("%s / Port%d / Slot%d"),
|
CString(c.carrierId.c_str()), port + 1, slot + 1);
|
|
CString gid = pGlass->getID().c_str();
|
gKeysWanted.insert(gid);
|
|
auto* rowG = FindChildByKey(pjNode, gid);
|
if (!rowG) {
|
m_listCtrl.InsertChild(pjNode, {
|
gid, _T("Glass"),
|
pGlass->getStateText().c_str(), _T(""),
|
carrier, _T("")
|
});
|
}
|
else {
|
if ((int)rowG->cols.size() < 6) rowG->cols.resize(6);
|
rowG->cols[1] = _T("Glass");
|
rowG->cols[2] = pGlass->getStateText().c_str();
|
rowG->cols[3] = _T("");
|
rowG->cols[4] = carrier;
|
rowG->cols[5] = _T("");
|
}
|
}
|
else {
|
CString key = _T("Null@") + CString(c.carrierId.c_str());
|
gKeysWanted.insert(key);
|
|
auto* rowG = FindChildByKey(pjNode, key);
|
if (!rowG) {
|
m_listCtrl.InsertChild(pjNode, {
|
key, _T("Glass"), _T(""), _T(""),
|
CString(c.carrierId.c_str()), _T("")
|
});
|
}
|
else {
|
if ((int)rowG->cols.size() < 6) rowG->cols.resize(6);
|
rowG->cols[1] = _T("Glass");
|
rowG->cols[2] = _T("");
|
rowG->cols[3] = _T("");
|
rowG->cols[4] = CString(c.carrierId.c_str());
|
rowG->cols[5] = _T("");
|
}
|
}
|
}
|
}
|
|
// 删除本次不存在的 Glass 子节点
|
RemoveStaleChildren(pjNode, gKeysWanted);
|
}
|
|
// 删除本次不存在的 PJ 子节点
|
RemoveStaleChildren(m_rootNode, pjKeysWanted);
|
|
// 3.4 重建可见行(不改变 expanded 标志,避免闪烁/折叠状态丢失)
|
m_listCtrl.RebuildVisible();
|
}
|
|
void CControlJobDlg::OnBnClickedButtonCompletionBath()
|
{
|
if (theApp.m_model.getMaster().forceCompleteControlJob("测试手动结批")) {
|
AfxMessageBox("结批完成");
|
}
|
|
OnBnClickedButtonReload();
|
}
|
|
void CControlJobDlg::OnBnClickedButtonReload()
|
{
|
auto* cj = m_pControlJob;
|
if (cj == nullptr) {
|
cj = theApp.m_model.getMaster().getControlJob();
|
}
|
|
ShowGroup1(cj == nullptr);
|
ShowGroup2(cj != nullptr);
|
GetDlgItem(IDC_BUTTON_COMPLETION_BATH)->EnableWindow(cj != nullptr);
|
LoadData(cj);
|
}
|
|
void CControlJobDlg::OnTimer(UINT_PTR nIDEvent)
|
{
|
if (1 == nIDEvent) {
|
OnBnClickedButtonReload();
|
}
|
|
CDialogEx::OnTimer(nIDEvent);
|
}
|