已添加2个文件
已修改5个文件
371 ■■■■■ 文件已修改
SourceCode/Bond/Servo/CEquipmentPage1.cpp 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipmentPage1.h 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/JobSlotGrid.cpp 86 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj.filters 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoMemDC.cpp 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoMemDC.h 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipmentPage1.cpp
@@ -9,12 +9,20 @@
// CEquipmentPage1 å¯¹è¯æ¡†
#define SIGNAL_GRID_ROWS 8
#define SIGNAL_GRID_COLS 8
#define SIGNAL_GRID_SIZE (SIGNAL_GRID_ROWS * SIGNAL_GRID_COLS)
#define TIMER_ID_SIGNAL_UPDATE  1001
#define TIMER_INTERVAL_MS       1000  // æ¯ 1 ç§’更新一次
IMPLEMENT_DYNAMIC(CEquipmentPage1, CHMPropertyPage)
CEquipmentPage1::CEquipmentPage1(CWnd* pParent /*=nullptr*/)
    : CHMPropertyPage(IDD_PAGE_EQUIPMENT1, pParent)
{
    m_pEquipment = nullptr;
    m_nCurrentDeviceID = 0;
}
CEquipmentPage1::~CEquipmentPage1()
@@ -31,6 +39,7 @@
    ON_WM_CTLCOLOR()
    ON_WM_DESTROY()
    ON_WM_SIZE()
    ON_WM_TIMER()
END_MESSAGE_MAP()
@@ -43,6 +52,149 @@
void CEquipmentPage1::setEquipment(SERVO::CEquipment* pEquipment)
{
    m_pEquipment = pEquipment;
    if (m_pEquipment != nullptr) {
        InitSignalListForDevice(pEquipment->getID());
    }
    else {
        ResetSignalPanel();
    }
}
void CEquipmentPage1::LoadSignalListFromCSV(const CString& strFilePath)
{
    m_mapSignalListByID.clear();
    CStdioFile file;
    if (!file.Open(strFilePath, CFile::modeRead | CFile::typeText)) {
        AfxMessageBox(_T("无法打开信号定义文件: ") + strFilePath);
        return;
    }
    CString strLine;
    int nLineNum = 0;
    while (file.ReadString(strLine)) {
        ++nLineNum;
        strLine.Trim();
        if (strLine.IsEmpty() || strLine.Left(1) == _T("#") || strLine.Left(2) == _T("//")) {
            continue; // è·³è¿‡æ³¨é‡Šè¡Œæˆ–空行
        }
        // åˆ†å‰²ä¸º tokens
        std::vector<CString> tokens;
        int curPos = 0;
        CString token = strLine.Tokenize(_T(","), curPos);
        while (!token.IsEmpty()) {
            token.Trim(); // åŽ»é™¤æ¯ä¸ªå­—æ®µçš„ç©ºæ ¼
            tokens.push_back(token);
            token = strLine.Tokenize(_T(","), curPos);
        }
        // æ ¼å¼æ£€æŸ¥
        if (tokens.size() < 3) {
            TRACE(_T("CSV æ ¼å¼é”™è¯¯ [行 %d]:字段数不足。\n"), nLineNum);
            continue;
        }
        // è§£æžå­—段
        int nDeviceID = _ttoi(tokens[0]);
        CString strSignalName = tokens[1];
        bool bClickable = _ttoi(tokens[2]) ? TRUE : FALSE;
        if (nDeviceID <= 0 || nDeviceID > 999 || strSignalName.IsEmpty()) {
            TRACE(_T("CSV è¡Œ %d:无效设备ID=%d æˆ–信号名为空\n"), nLineNum, nDeviceID);
            continue;
        }
        SignalInfo info = { strSignalName, false, bClickable };
        m_mapSignalListByID[nDeviceID].push_back(info);
    }
    file.Close();
    TRACE(_T("信号定义加载完成,共 %d ä¸ªè®¾å¤‡ã€‚\n"), (int)m_mapSignalListByID.size());
}
void CEquipmentPage1::InitSignalListForDevice(int nDeviceID)
{
    m_nCurrentDeviceID = nDeviceID;
    m_vSignalList.clear();
    auto it = m_mapSignalListByID.find(nDeviceID);
    if (it != m_mapSignalListByID.end()) {
        m_vSignalList = it->second;
    }
    else {
        TRACE(_T("Warning: No signals found for DeviceID=%d\n"), nDeviceID);
    }
    // è¡¥é½åˆ° 64 é¡¹
    while (m_vSignalList.size() < SIGNAL_GRID_SIZE) {
        m_vSignalList.push_back({ _T(""), false, false });
    }
    InitSignalSlotTextAndClickable();
    UpdateAllSignalStatesFromDevice();
}
void CEquipmentPage1::UpdateSignalState(int nRow, int nCol, bool bNewState)
{
    if (!::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) {
        return;
    }
    int nIndex = nRow * SIGNAL_GRID_COLS + nCol;
    if (nIndex < 0 || nIndex >= static_cast<int>(m_vSignalList.size())) {
        return;
    }
    if (m_vSignalList[nIndex].bCurrentState != bNewState) {
        m_vSignalList[nIndex].bCurrentState = bNewState;
        m_ctrlSignalPanel.SetSlotStatus(nRow, nCol, bNewState);
        TRACE(_T("[Device %d] UpdateSignalState: [%d, %d] = %d\n"), m_nCurrentDeviceID, nRow, nCol, bNewState);
    }
}
void CEquipmentPage1::InitSignalSlotTextAndClickable()
{
    if (!::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) {
        return;
    }
    for (int i = 0; i < SIGNAL_GRID_SIZE; ++i) {
        int nRow = i / SIGNAL_GRID_COLS;
        int nCol = i % SIGNAL_GRID_COLS;
        const auto& signal = m_vSignalList[i];
        m_ctrlSignalPanel.SetSlotText(nRow, nCol, signal.strName);
        m_ctrlSignalPanel.SetSlotClickable(nRow, nCol, signal.bClickable);
    }
}
void CEquipmentPage1::UpdateAllSignalStatesFromDevice()
{
    if (!m_pEquipment) {
        return;
    }
    for (int nRow = 0; nRow < SIGNAL_GRID_ROWS; ++nRow) {
        for (int nCol = 0; nCol < SIGNAL_GRID_COLS; ++nCol) {
            BOOL bCurrentState = m_pEquipment->isLinkSignalOn(nRow, nCol);
            UpdateSignalState(nRow, nCol, bCurrentState);
        }
    }
}
void CEquipmentPage1::ResetSignalPanel()
{
    if (!::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) {
        return;
    }
    m_ctrlSignalPanel.ClearAll();
    m_vSignalList.clear();
    m_nCurrentDeviceID = 0;
}
BOOL CEquipmentPage1::OnInitDialog()
@@ -50,6 +202,34 @@
    CHMPropertyPage::OnInitDialog();
    // TODO:  åœ¨æ­¤æ·»åŠ é¢å¤–çš„åˆå§‹åŒ–
    m_ctrlSignalPanel.Create(AfxRegisterWndClass(0), _T("SignalGrid"), WS_CHILD | WS_VISIBLE, CRect(0, 0, 100, 100), this, 1002);
    m_ctrlSignalPanel.SetColors(RGB(0, 200, 0), RGB(220, 220, 220));
    m_ctrlSignalPanel.SetGridSize(SIGNAL_GRID_ROWS, SIGNAL_GRID_COLS);
    m_ctrlSignalPanel.SetTextFont(_T("Microsoft YaHei"), 10);
    m_ctrlSignalPanel.SetSlotClickCallback([this](int nRow, int nCol) {
        int index = nRow * SIGNAL_GRID_COLS + nCol;
        if (index >= 0 && index < (int)m_vSignalList.size() && m_vSignalList[index].bClickable && m_pEquipment != nullptr) {
            // è¯»å–当前状态并切换
            const BOOL bCurrentState = m_pEquipment->isLinkSignalOn(nRow, nCol);
            m_pEquipment->setLinkSignal(nRow, nCol, !bCurrentState);
        }
    });
    {
        TCHAR szPath[MAX_PATH] = {};
        GetModuleFileName(nullptr, szPath, MAX_PATH);
        PathRemoveFileSpec(szPath);
        CString strCSVFile = CString(szPath) + _T("\\Config\\signals.csv");
        LoadSignalListFromCSV(strCSVFile);
    }
    // å¦‚果设备已设置,则初始化信号列表
    if (m_pEquipment != nullptr) {
        InitSignalListForDevice(m_pEquipment->getID());
    }
    KillTimer(TIMER_ID_SIGNAL_UPDATE);
    SetTimer(TIMER_ID_SIGNAL_UPDATE, TIMER_INTERVAL_MS, nullptr);
    return TRUE;  // return TRUE unless you set the focus to a control
                  // å¼‚常: OCX å±žæ€§é¡µåº”返回 FALSE
@@ -70,6 +250,10 @@
    CHMPropertyPage::OnDestroy();
    // TODO: åœ¨æ­¤å¤„添加消息处理程序代码
    KillTimer(TIMER_ID_SIGNAL_UPDATE);
    if (::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) {
        m_ctrlSignalPanel.DestroyWindow();
    }
}
void CEquipmentPage1::OnSize(UINT nType, int cx, int cy)
@@ -77,4 +261,21 @@
    CHMPropertyPage::OnSize(nType, cx, cy);
    // TODO: åœ¨æ­¤å¤„添加消息处理程序代码
    if (::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) {
        CRect rc;
        GetClientRect(&rc);
        rc.DeflateRect(10, 10);
        m_ctrlSignalPanel.MoveWindow(rc);
    }
}
void CEquipmentPage1::OnTimer(UINT_PTR nIDEvent)
{
    if (nIDEvent == TIMER_ID_SIGNAL_UPDATE) {
        if (m_pEquipment && !m_vSignalList.empty()) {
            UpdateAllSignalStatesFromDevice();
        }
    }
    CHMPropertyPage::OnTimer(nIDEvent);
}
SourceCode/Bond/Servo/CEquipmentPage1.h
@@ -1,9 +1,17 @@
#pragma once
#include "CHMPropertyPage.h"
#include "CEquipment.h"
#include "JobSlotGrid.h"
// CEquipmentPage1 å¯¹è¯æ¡†
struct SignalInfo
{
    CString strName;       // ä¿¡å·åç§°
    bool bCurrentState;    // å½“前状态(ON/OFF)
    bool bClickable;       // æ˜¯å¦å…è®¸ç‚¹å‡»
};
class CEquipmentPage1 : public CHMPropertyPage
{
@@ -16,7 +24,18 @@
    void setEquipment(SERVO::CEquipment* pEquipment);
private:
    int m_nCurrentDeviceID;
    SERVO::CEquipment* m_pEquipment;
    std::vector<SignalInfo> m_vSignalList;
    CJobSlotGrid m_ctrlSignalPanel;
    std::map<int, std::vector<SignalInfo>> m_mapSignalListByID;
    void LoadSignalListFromCSV(const CString& strFilePath);
    void InitSignalListForDevice(int nDeviceID);
    void UpdateSignalState(int nRow, int nCol, bool bNewState);
    void InitSignalSlotTextAndClickable();
    void UpdateAllSignalStatesFromDevice();
    void ResetSignalPanel();
// å¯¹è¯æ¡†æ•°æ®
@@ -33,4 +52,5 @@
    afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
    afx_msg void OnDestroy();
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnTimer(UINT_PTR nIDEvent);
};
SourceCode/Bond/Servo/JobSlotGrid.cpp
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "JobSlotGrid.h"
#include "ServoMemDC.h"
#ifdef _DEBUG
#define new DEBUG_NEW
@@ -221,17 +222,32 @@
void CJobSlotGrid::OnPaint() {
    CPaintDC dc(this);
    DrawGrid(&dc);
    CServoMemDC memDC(&dc);
    DrawGrid(&memDC);
}
void CJobSlotGrid::DrawGrid(CDC* pDC)
{
    CRect rect;
    GetClientRect(&rect);
    pDC->FillSolidRect(&rect, ::GetSysColor(COLOR_3DFACE)); // å®‰å…¨èƒŒæ™¯è‰²ï¼Œä»…内存绘制
    if (m_nCols == 0 || m_nRows == 0) {
        return;
    }
    // è®¡ç®—格子尺寸
    int nCellWidth = rect.Width() / m_nCols;
    int nCellHeight = rect.Height() / m_nRows;
    // å­—体设置
    CFont* pOldFont = pDC->SelectObject(&m_fontText);
    pDC->SetBkMode(TRANSPARENT);
    pDC->SetTextColor(RGB(0, 0, 0));
    // å®šä¹‰é¢œè‰²å¸¸é‡
    constexpr COLORREF COLOR_HOVER = RGB(200, 230, 255);
    constexpr COLORREF COLOR_CLICK = RGB(0, 120, 215);
    for (int i = 0; i < m_nRows; ++i) {
        for (int j = 0; j < m_nCols; ++j) {
@@ -241,29 +257,61 @@
            bool bIsHover = (m_ptHover.x == j && m_ptHover.y == i);
            bool bIsClicking = bIsHover && m_bLButtonDown;
            // é€‰æ‹©é¢œè‰²
            COLORREF fillColor;
            if (bIsClicking) {
                fillColor = RGB(0, 120, 215);   // é¼ æ ‡æŒ‰ä¸‹è‰²
            }
            else if (bIsHover) {
                fillColor = RGB(200, 230, 255); // æ‚¬åœé«˜äº®
            }
            else {
                fillColor = m_vSlotStatus[i][j] ? m_colorHasJob : m_colorNoJob;
            // é€‰æ‹©å¡«å……颜色
            COLORREF fillColor = m_vSlotStatus[i][j] ? m_colorHasJob : m_colorNoJob;
            if (IsSlotClickable(i, j)) {
                if (bIsClicking) {
                    fillColor = COLOR_CLICK;
                }
                else if (bIsHover) {
                    fillColor = COLOR_HOVER;
                }
            }
            // ç”»èƒŒæ™¯
            CBrush brush(fillColor);
            pDC->FillRect(&cellRect, &brush);
            // ç»˜åˆ¶èƒŒæ™¯ï¼ˆé«˜æ•ˆæ›¿ä»£ CBrush)
            pDC->FillSolidRect(&cellRect, fillColor);
            // è¾¹æ¡†
            // ç»˜åˆ¶è¾¹æ¡†
            pDC->DrawEdge(&cellRect, EDGE_SUNKEN, BF_RECT);
            // æ–‡æœ¬
            pDC->SetBkMode(TRANSPARENT);
            pDC->SetTextColor(RGB(0, 0, 0));
            pDC->DrawText(m_vSlotText[i][j], &cellRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
            // å¦‚果是可点击格子,在左上角画一个小圆点
            if (IsSlotClickable(i, j)) {
                constexpr int DOT_RADIUS = 3;
                int cx = cellRect.left + 5;
                int cy = cellRect.top + 5;
                // ä¿å­˜æ—§ç¬”和刷子
                CBrush brushDot(RGB(0, 120, 215));
                CBrush* pOldBrush = pDC->SelectObject(&brushDot);
                CPen penDot(PS_SOLID, 1, RGB(0, 120, 215));
                CPen* pOldPen = pDC->SelectObject(&penDot);
                // ç»˜åˆ¶åœ†ç‚¹
                pDC->Ellipse(cx - DOT_RADIUS, cy - DOT_RADIUS, cx + DOT_RADIUS, cy + DOT_RADIUS);
                // æ¢å¤
                pDC->SelectObject(pOldBrush);
                pDC->SelectObject(pOldPen);
            }
            // èŽ·å–æ–‡å­—ï¼ˆå®‰å…¨ï¼‰
            CString strText;
            if (i < m_vSlotText.size() && j < m_vSlotText[i].size()) {
                strText = m_vSlotText[i][j];
            }
            if (!strText.IsEmpty()) {
                // å…ˆè®¡ç®—文字高度(支持换行)
                CRect calcRect = cellRect;
                pDC->DrawText(strText, &calcRect, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_CALCRECT);
                // é‡æ–°è®¾å®šå±…中绘制区域
                CRect textRect = cellRect;
                textRect.top += (cellRect.Height() - calcRect.Height()) / 2;
                // å®žé™…绘制文字
                pDC->DrawText(strText, &textRect, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX);
            }
        }
    }
SourceCode/Bond/Servo/Servo.vcxproj
@@ -319,6 +319,7 @@
    <ClInclude Include="ServoCommo.h" />
    <ClInclude Include="ServoDlg.h" />
    <ClInclude Include="ServoGraph.h" />
    <ClInclude Include="ServoMemDC.h" />
    <ClInclude Include="stdafx.h" />
    <ClInclude Include="targetver.h" />
    <ClInclude Include="TerminalDisplayDlg.h" />
@@ -444,6 +445,7 @@
    <ClCompile Include="Servo.cpp" />
    <ClCompile Include="ServoDlg.cpp" />
    <ClCompile Include="ServoGraph.cpp" />
    <ClCompile Include="ServoMemDC.cpp" />
    <ClCompile Include="stdafx.cpp">
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -157,6 +157,7 @@
    <ClCompile Include="CSlot.cpp" />
    <ClCompile Include="CRobotTaskDlg.cpp" />
    <ClCompile Include="PageTransferLog.cpp" />
    <ClCompile Include="ServoMemDC.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="AlarmManager.h" />
@@ -319,6 +320,7 @@
    <ClInclude Include="CSlot.h" />
    <ClInclude Include="CRobotTaskDlg.h" />
    <ClInclude Include="PageTransferLog.h" />
    <ClInclude Include="ServoMemDC.h" />
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="Servo.rc" />
SourceCode/Bond/Servo/ServoMemDC.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
#include "stdafx.h"
#include "ServoMemDC.h"
CServoMemDC::CServoMemDC(CDC* pDC, const CRect* pRect)
    : CDC(), m_pOldBitmap(nullptr), m_pDC(pDC), m_bMemDC(FALSE)
{
    ASSERT(pDC != nullptr);
    if (pRect == nullptr)
        pDC->GetClipBox(&m_rect);
    else
        m_rect = *pRect;
    if (CreateCompatibleDC(pDC)) {
        m_bMemDC = TRUE;
        m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
        m_pOldBitmap = SelectObject(&m_bitmap);
        SetWindowOrg(m_rect.left, m_rect.top);
    }
}
CServoMemDC::~CServoMemDC()
{
    if (m_bMemDC) {
        // å°†å†…å­˜ DC æ‹·è´å›žåŽŸå§‹çª—å£ DC
        m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),
            this, m_rect.left, m_rect.top, SRCCOPY);
        SelectObject(m_pOldBitmap);
    }
}
CServoMemDC* CServoMemDC::operator->()
{
    return this;
}
CServoMemDC::operator CDC* ()
{
    return this;
}
SourceCode/Bond/Servo/ServoMemDC.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
#pragma once
#include <afxwin.h>  // MFC æ ¸å¿ƒå¤´æ–‡ä»¶
// CServoMemDC:双缓冲绘图封装类(消除闪烁)
class CServoMemDC : public CDC
{
public:
    CServoMemDC(CDC* pDC, const CRect* pRect = nullptr); // æž„造:传入目标 DC å’ŒåŒºåŸŸ
    ~CServoMemDC();                                      // æžæž„:自动拷贝到目标 DC
    CServoMemDC* operator->();
    operator CDC* ();
private:
    CBitmap m_bitmap;        // ç”¨äºŽå†…å­˜ DC çš„位图
    CBitmap* m_pOldBitmap;   // æ—§ä½å›¾
    CDC* m_pDC;              // åŽŸå§‹ç»˜å›¾ DC(窗口)
    CRect m_rect;            // ç»˜å›¾åŒºåŸŸ
    BOOL m_bMemDC;           // æ˜¯å¦å¯ç”¨äº†å†…å­˜ DC(CreateCompatibleDC æˆåŠŸï¼‰
};