LAPTOP-SNT8I5JK\Boounion
2025-03-21 980d4fc1690b4f8a81dc65e8573d2898f34a406f
1.加入连接图示
已添加14个文件
已修改16个文件
5332 ■■■■■ 文件已修改
SourceCode/Bond/Servo/CBonder.cpp 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CBonder.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipment.cpp 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipment.h 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CLoadPort.cpp 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CMaster.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageGraph1.cpp 406 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageGraph1.h 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageGraph2.cpp 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageGraph2.h 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPanel.cpp 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPanel.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPin.cpp 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ColorTransfer.cpp 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ColorTransfer.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Common.h 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Context.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/EqsGraphWnd.cpp 2377 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/EqsGraphWnd.h 234 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/HmTab.cpp 529 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/HmTab.h 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/MapPosWnd.cpp 536 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/MapPosWnd.h 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.cpp 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.rc 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj.filters 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoDlg.cpp 352 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoDlg.h 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/resource.h 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CBonder.cpp
@@ -48,6 +48,26 @@
        CEquipment::serialize(ar);
    }
    void CBonder::getAttributeVector(CAttributeVector& attrubutes)
    {
        __super::getAttributeVector(attrubutes);
        for (auto item : m_inputPins) {
            attrubutes.addAttribute(new CAttribute(item->getName().c_str(),
                std::to_string((int)item->getType()).c_str(), ""));
        }
        for (auto item : m_outputPins) {
            attrubutes.addAttribute(new CAttribute(item->getName().c_str(),
                std::to_string((int)item->getType()).c_str(), ""));
        }
        for (auto item : m_panelList) {
            attrubutes.addAttribute(new CAttribute("Panel",
                item->getID().c_str(), ""));
        }
    }
    int CBonder::recvIntent(CPin* pPin, CIntent* pIntent)
    {
        ASSERT(pPin);
@@ -58,7 +78,6 @@
        CEquipment* pFromEq = pFromPin->getEquipment();
        ASSERT(pFromEq);
        LOGI("<CBonder><%s-%s>收到来自<%s.%s>的Intent<%d,%s,0x%x>", 
            this->getName().c_str(), 
            pPin->getName().c_str(),
@@ -66,9 +85,53 @@
            pFromPin->getName().c_str(),
            pIntent->getCode(),
            pIntent->getMsg(),
            pIntent->getContext()
            );
            pIntent->getContext());
        return 0;
        // ä»¥ä¸‹è§£é‡Šå¤„理数据
        int code = pIntent->getCode();
        // æµ‹è¯•
        if (code == FLOW_TEST) {
            AfxMessageBox(pIntent->getMsg());
            return FLOW_ACCEPT;
        }
        // ä¿¡å·
        if (code == FLOW_SIGNAL) {
            return FLOW_ACCEPT;
        }
        // æ•°æ®
        if (code == FLOW_SIGNAL) {
            return FLOW_ACCEPT;
        }
        // ç‰©æ–™
        if (code == FLOW_MOVE_MATERIAL) {
            // å¦‚果我这里是空的,可以接受
            Lock();
            if (m_panelList.size() < 15) {
                CPanel* pPanel = (CPanel*)pIntent->getContext();
                ASSERT(pPanel);
                pPanel->addRef();
                m_panelList.push_back(pPanel);
                Unlock();
                return FLOW_ACCEPT;
            }
            else {
                Unlock();
                return FLOW_REJECT;
            }
        }
        return FLOW_ACCEPT;
    }
}
SourceCode/Bond/Servo/CBonder.h
@@ -17,6 +17,7 @@
        virtual void initPins();
        virtual void onTimer(UINT nTimerid);
        virtual void serialize(CArchive& ar);
        virtual void getAttributeVector(CAttributeVector& attrubutes);
        virtual int recvIntent(CPin* pPin, CIntent* pIntent);
    };
}
SourceCode/Bond/Servo/CEquipment.cpp
@@ -437,4 +437,14 @@
    {
        return 0;
    }
    void CEquipment::addPanelToList(CPanel* pPanel)
    {
        ASSERT(pPanel);
        Lock();
        pPanel->addRef();
        m_panelList.push_back(pPanel);
        Unlock();
    }
}
SourceCode/Bond/Servo/CEquipment.h
@@ -15,6 +15,8 @@
#include "CEqVCREnableStep.h"
#include <vector>
#include <map>
#include <list>
#include "CPanel.h"
namespace SERVO {
@@ -107,6 +109,7 @@
    protected:
        inline void Lock() { EnterCriticalSection(&m_criticalSection); }
        inline void Unlock() { LeaveCriticalSection(&m_criticalSection); }
        void addPanelToList(CPanel* pPanel);
    protected:
        EquipmentListener m_listener;
@@ -119,6 +122,8 @@
        MemoryBlock m_blockWriteBit;
        std::vector<CPin*> m_inputPins;
        std::vector<CPin*> m_outputPins;
        std::list<CPanel*> m_panelList;
        // ä»¥ä¸‹ä¸ºä»ŽCC-Link读取到的Bit标志位
    private:
SourceCode/Bond/Servo/CLoadPort.cpp
@@ -61,15 +61,48 @@
            attrubutes.addAttribute(new CAttribute(item->getName().c_str(),
                std::to_string((int)item->getType()).c_str(), ""));
        }
        for (auto item : m_panelList) {
            attrubutes.addAttribute(new CAttribute("Panel",
                item->getID().c_str(), ""));
        }
    }
    void CLoadPort::outputPanel()
    {
        CPin* pOutPin = getPin("Out");
        CIntent intent;
        intent.setCode(1);
        intent.setMsg("Hello");
        pOutPin->sendIntent(&intent);
        // å¦‚果列表中没有Panel,模拟生成10å¼ 
        if (m_panelList.empty()) {
            static int ii = 0;
            char szBuffer[64];
            LOGI("<CLoadPort>模拟生成10å¼ PANEL");
            for (int i = 0; i < 10; i++) {
                sprintf_s(szBuffer, "P20250320A1A%d", ++ii);
                CPanel* pPanel = new CPanel();
                pPanel->setID(szBuffer);
                addPanelToList(pPanel);
            }
        }
        // æ¨¡æ‹Ÿå–出第一张Panel,传送到下一环节
        Lock();
        CPanel* pContext = m_panelList.front();
        pContext->addRef();
        CIntent intent(FLOW_MOVE_MATERIAL, "", pContext);
        int nRet = pOutPin->sendIntent(&intent);
        if (nRet == FLOW_REJECT) {
            AfxMessageBox("对方拒绝接受");
        }
        else if (nRet == FLOW_ACCEPT) {
            m_panelList.pop_front();
            pContext->release();        // æ·»åŠ åˆ°åˆ—é˜Ÿæ—¶addRef, å–出时release
        }
        pContext->release();
        Unlock();
    }
}
SourceCode/Bond/Servo/CMaster.cpp
@@ -509,7 +509,7 @@
            AfxMessageBox("连接失败");
        }
        else {
            AfxMessageBox("连接成功");
            // AfxMessageBox("连接成功");
        }
    }
}
SourceCode/Bond/Servo/CPageGraph1.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,406 @@
// CPageGraph1.cpp: å®žçŽ°æ–‡ä»¶
//
#include "stdafx.h"
#include "Servo.h"
#include "CPageGraph1.h"
#include "afxdialogex.h"
#include "Common.h"
// Image
#define IMAGE_ROBOT                2
#define INDICATE_BONDER1        1
#define INDICATE_BONDER2        2
#define INDICATE_FLIPER            3
#define INDICATE_ALIGNER        4
#define INDICATE_LPORT4            5
#define INDICATE_LPORT3            6
#define INDICATE_LPORT2            7
#define INDICATE_LPORT1            8
#define INDICATE_ROBOT_ARM1        9
#define INDICATE_ROBOT_ARM2        10
#define INDICATE_VACUUM_BAKE    11
#define INDICATE_BAKE_COOLING    12
#define INDICATE_MEASUREMENT    13
// CPageGraph1 å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CPageGraph1, CDialogEx)
CPageGraph1::CPageGraph1(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_PAGE_GRAPH1, pParent)
{
    m_pGraph = nullptr;
    m_pObserver = nullptr;
    m_bIsRobotMoving = FALSE;
    m_crBkgnd = PAGE_GRPAH1_BACKGROUND_COLOR;
    m_hbrBkgnd = nullptr;
}
CPageGraph1::~CPageGraph1()
{
}
void CPageGraph1::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CPageGraph1, CDialogEx)
    ON_WM_CTLCOLOR()
    ON_WM_DESTROY()
    ON_WM_SIZE()
    ON_NOTIFY(BYSERVOGRAPH_ITEM_CLICKED, IDC_SERVO_GRAPH1, &CPageGraph1::OnGraphItemClicked)
    ON_WM_ERASEBKGND()
    ON_WM_TIMER()
END_MESSAGE_MAP()
// CPageGraph1 æ¶ˆæ¯å¤„理程序
void CPageGraph1::InitRxWindows()
{
    /* code */
    // è®¢é˜…数据
    IRxWindows* pRxWindows = RX_GetRxWindows();
    pRxWindows->enableLog(5);
    if (m_pObserver == NULL) {
        m_pObserver = pRxWindows->allocObserver([&](IAny* pAny) -> void {
            // onNext
            pAny->addRef();
            int code = pAny->getCode();
            if (RX_CODE_EQ_ALIVE == code) {
                // é€šçŸ¥è®¾å¤‡çŠ¶æ€
                SERVO::CEquipment* pEquipment = nullptr;
                if (pAny->getPtrValue("ptr", (void*&)pEquipment)) {
                    if (pEquipment != nullptr) {
                        int nID = pEquipment->getID();
                        BOOL bAlive = pEquipment->isAlive();
                        if (EQ_ID_EFEM == nID) {
                            DeviceStatus status = bAlive ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
                            UpdateDeviceStatus(INDICATE_ROBOT_ARM1, status);
                            UpdateDeviceStatus(INDICATE_ROBOT_ARM2, status);
                        }
                    }
                }
            }
            pAny->release();
            }, [&]() -> void {
                // onComplete
            }, [&](IThrowable* pThrowable) -> void {
                // onErrorm
                pThrowable->printf();
            });
        theApp.m_model.getObservable()->observeOn(pRxWindows->mainThread())
            ->subscribe(m_pObserver);
    }
}
BOOL CPageGraph1::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    SetTimer(1, 3000, nullptr);
    // å›¾ç¤º
    m_pGraph = CServoGraph::Hook(GetDlgItem(IDC_SERVO_GRAPH1)->GetSafeHwnd());
    CString strPath;
    strPath.Format(_T("%s\\res\\Servo001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
    m_pGraph->AddImage(1, (LPTSTR)(LPCTSTR)strPath, 0, 0);
    strPath.Format(_T("%s\\res\\Robot001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
    m_pGraph->AddImage(IMAGE_ROBOT, (LPTSTR)(LPCTSTR)strPath, 170, 270);
    // æ·»åŠ æŒ‡ç¤ºå™¨
    // Bonder
    m_pGraph->AddIndicateBox(INDICATE_BONDER1, 220, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_BONDER1, "10", "Bonder 1");
    m_pGraph->AddIndicateBox(INDICATE_BONDER2, 220, 516, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_BONDER2, "11", "Bonder 2");
    // ç¿»è½¬
    m_pGraph->AddIndicateBox(INDICATE_FLIPER, 338, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_FLIPER, "8", "Fliper");
    // å¯¹ä½
    m_pGraph->AddIndicateBox(INDICATE_ALIGNER, 428, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_ALIGNER, "7", "Aligner");
    // Load port 4
    m_pGraph->AddIndicateBox(INDICATE_LPORT4, 518, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT4, "4", "LPort4");
    // Load port 3
    m_pGraph->AddIndicateBox(INDICATE_LPORT3, 606, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT3, "3", "LPort3");
    // Load port 2
    m_pGraph->AddIndicateBox(INDICATE_LPORT2, 690, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT2, "2", "LPort2");
    // Load port 1
    m_pGraph->AddIndicateBox(INDICATE_LPORT1, 774, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT1, "1", "LPort1");
    // Robot
    m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM1, 190, 294, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_ROBOT_ARM1, "5", "Robot");
    m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM2, 243, 294, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_ROBOT_ARM2, "6", "Robot");
    // Vacuum bake
    m_pGraph->AddIndicateBox(INDICATE_VACUUM_BAKE, 396, 516, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_VACUUM_BAKE, "9", "Vacuum bake");
    // Bake cooling
    m_pGraph->AddIndicateBox(INDICATE_BAKE_COOLING, 566, 516, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_BAKE_COOLING, "12", "Bake cooling");
    // ç²¾åº¦æ£€
    m_pGraph->AddIndicateBox(INDICATE_MEASUREMENT, 737, 516, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_MEASUREMENT, "13", "Measurement");
    // ç»‘定数据
    {
        SERVO::CEquipment* pEquipment = theApp.m_model.m_master.getEquipment(EQ_ID_EFEM);
        m_pGraph->SetIndicateBoxData(INDICATE_ROBOT_ARM1, pEquipment);
    }
    return TRUE;  // return TRUE unless you set the focus to a control
                  // å¼‚常: OCX å±žæ€§é¡µåº”返回 FALSE
}
void CPageGraph1::UpdateDeviceStatus(int id, DeviceStatus status)
{
    // æ ¹æ®çŠ¶æ€è®¾ç½®é¢œè‰²
    COLORREF newBackgroundColor;
    COLORREF newFrameColor1;
    COLORREF newFrameColor2;
    switch (status) {
    case ONLINE:
        newBackgroundColor = EQ_BOX_ONLINE;
        newFrameColor1 = EQ_BOX_FRAME1;
        newFrameColor2 = EQ_BOX_FRAME2;
        break;
    case OFFLINE:
        newBackgroundColor = RGB(222, 222, 222);
        newFrameColor1 = EQ_BOX_FRAME1;
        newFrameColor2 = EQ_BOX_FRAME2;
        break;
    default:
        newBackgroundColor = RGB(255, 255, 255); // é»˜è®¤ç™½è‰²èƒŒæ™¯
        newFrameColor1 = RGB(0, 0, 0);           // é»˜è®¤é»‘色框架1
        newFrameColor2 = RGB(0, 0, 0);           // é»˜è®¤é»‘色框架2
        break;
    }
    m_pGraph->UpdateIndicateBox1Colors(id, newBackgroundColor, newFrameColor1, newFrameColor2);
    // åˆ·æ–°ç•Œé¢
    Invalidate();
    UpdateWindow();
}
BOOL CPageGraph1::OnEraseBkgnd(CDC* pDC)
{
    // TODO: åœ¨æ­¤æ·»åŠ æ¶ˆæ¯å¤„ç†ç¨‹åºä»£ç å’Œ/或调用默认值
    if (m_bIsRobotMoving) {
        // ç¦æ­¢åˆ·æ–°èƒŒæ™¯ï¼Œé¿å…é—ªçƒ
        return TRUE;
    }
    return CDialogEx::OnEraseBkgnd(pDC);
}
HBRUSH CPageGraph1::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
    if (nCtlColor == CTLCOLOR_STATIC) {
        pDC->SetBkColor(m_crBkgnd);
        pDC->SetTextColor(RGB(0, 0, 0));
    }
    if (m_hbrBkgnd == nullptr) {
        m_hbrBkgnd = CreateSolidBrush(m_crBkgnd);
    }
    return m_hbrBkgnd;
}
void CPageGraph1::OnDestroy()
{
    CDialogEx::OnDestroy();
    if (m_hbrBkgnd != nullptr) {
        ::DeleteObject(m_hbrBkgnd);
    }
}
void CPageGraph1::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);
    if (GetDlgItem(IDC_SERVO_GRAPH1) == nullptr) return;
    CRect rcClient;
    GetClientRect(&rcClient);
    GetDlgItem(IDC_SERVO_GRAPH1)->MoveWindow(0, 0, rcClient.Width(), rcClient.Height());
}
void CPageGraph1::UpdateRobotPosition(float percentage)
{
    // é™åˆ¶ç™¾åˆ†æ¯”范围在 [0, 1] ä¹‹é—´
    if (percentage < 0.0f) percentage = 0.0f;
    if (percentage > 1.0f) percentage = 1.0f;
    // æ ¹æ®ç™¾åˆ†æ¯”计算目标 X åæ ‡
    int startX = m_pGraph->GetImage(IMAGE_ROBOT)->x;
    int endX = static_cast<int>(170 + percentage * (700 - 170));
    int arm1Offset = 20;  // ä»Žå›¾ç‰‡åˆ°ARM1的偏移
    int arm2Offset = 73;  // ä»Žå›¾ç‰‡åˆ°ARM2的偏移
    // è®¡ç®—移动所需的时间
    int distance = abs(endX - startX);
    int duration = static_cast<int>((distance / 100.0) * 1000);
    auto startTime = std::chrono::steady_clock::now();
    auto endTime = startTime + std::chrono::milliseconds(duration);
    // å¼€å§‹ç§»åŠ¨ï¼Œè®¾ç½®æ ‡è®°
    m_bIsRobotMoving = TRUE;
    // å¼€å§‹å¹³æ»‘移动
    while (std::chrono::steady_clock::now() < endTime) {
        auto currentTime = std::chrono::steady_clock::now();
        float progress = std::chrono::duration<float, std::milli>(currentTime - startTime).count() / duration;
        progress = min(progress, 1.0f);
        // æ ¹æ®è¿›åº¦è®¡ç®—当前位置
        int currentX = static_cast<int>(startX + progress * (endX - startX));
        m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, currentX, 270);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, currentX + arm1Offset, 294);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, currentX + arm2Offset, 294);
        // åˆ·æ–°ç•Œé¢
        Invalidate();
        UpdateWindow();
        // æŽ§åˆ¶å¸§çŽ‡çº¦ä¸º 60 FPS
        std::this_thread::sleep_for(std::chrono::milliseconds(16));
    }
    // ç¡®ä¿æœ€åŽä½ç½®ç²¾ç¡®åˆ°ç›®æ ‡ä½ç½®
    m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, endX, 270);
    m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, endX + arm1Offset, 294);
    m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, endX + arm2Offset, 294);
    // ç•Œé¢é‡ç»˜
    Invalidate();
    // åŠ¨ç”»ç»“æŸï¼Œè®¾ç½®æ ‡è®°
    m_bIsRobotMoving = FALSE;
}
void CPageGraph1::RotateRobot(float angleInDegrees)
{
    // å°†è§’度转换为弧度
    float angleInRadians = static_cast<float>(std::acos(-1)) / 180.0f * angleInDegrees;
    // èŽ·å–æœºå™¨äººå›¾ç‰‡çš„å½“å‰åæ ‡å’Œä¸­å¿ƒ
    auto* pImage = m_pGraph->GetImage(IMAGE_ROBOT);
    if (!pImage) return;
    // æ›´æ–° Rotate å›¾ç‰‡çš„角度,确保角度保持在 [0, 360) èŒƒå›´å†…
    m_pGraph->UpdateImageAngle(IMAGE_ROBOT, static_cast<float>(fmod(pImage->angle + angleInDegrees + 360, 360)));
    int cx = pImage->x + pImage->bmWidth / 2;  // å›¾ç‰‡ä¸­å¿ƒ X
    int cy = pImage->y + pImage->bmHeight / 2; // å›¾ç‰‡ä¸­å¿ƒ Y
    // æ—‹è½¬æŒ‡ç¤ºæ¡†çš„坐标
    auto* pRobot1 = m_pGraph->GetIndicateBox(INDICATE_ROBOT_ARM1);
    auto* pRobot2 = m_pGraph->GetIndicateBox(INDICATE_ROBOT_ARM2);
    if (pRobot1 && pRobot2) {
        int newArmX1 = pImage->x + 20;
        int newArmY1 = 294;
        int newArmX2 = pImage->x + 73;
        int newArmY2 = 294;
        if (angleInDegrees != 0.0f) {
            // è®¡ç®—指示框1的新坐标
            newArmX1 = static_cast<int>(cx + (pRobot1->x - cx) * cos(angleInRadians) - (pRobot1->y - cy) * sin(angleInRadians));
            newArmY1 = static_cast<int>(cy + (pRobot1->x - cx) * sin(angleInRadians) + (pRobot1->y - cy) * cos(angleInRadians));
            // è®¡ç®—指示框2的新坐标
            newArmX2 = static_cast<int>(cx + (pRobot2->x - cx) * cos(angleInRadians) - (pRobot2->y - cy) * sin(angleInRadians));
            newArmY2 = static_cast<int>(cy + (pRobot2->x - cx) * sin(angleInRadians) + (pRobot2->y - cy) * cos(angleInRadians));
        }
        // æ›´æ–°æŒ‡ç¤ºæ¡†çš„位置
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, newArmX1, newArmY1);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, newArmX2, newArmY2);
    }
    // å¼ºåˆ¶é‡ç»˜ç•Œé¢
    Invalidate();
}
void CPageGraph1::OnGraphItemClicked(NMHDR* pNMHDR, LRESULT* pResult)
{
    BYSERVOGRAPH_NMHDR* pGraphNmhdr = reinterpret_cast<BYSERVOGRAPH_NMHDR*>(pNMHDR);
    CString s; s.Format(_T("OnGraphItemClicked %d"), pGraphNmhdr->dwData);
    SERVO::CEquipment* pEquipment = (SERVO::CEquipment*)m_pGraph->GetIndicateBoxData(pGraphNmhdr->dwData);
    if (pEquipment != nullptr) {
        AfxMessageBox(pEquipment->getName().c_str());
    }
    *pResult = 0;
}
void CPageGraph1::OnTimer(UINT_PTR nIDEvent)
{
    if (1 == nIDEvent) {
        KillTimer(1);
        InitRxWindows();
    }
    CDialogEx::OnTimer(nIDEvent);
}
SourceCode/Bond/Servo/CPageGraph1.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
#pragma once
#include "ServoGraph.h"
enum DeviceStatus {
    ONLINE,       // åœ¨çº¿
    OFFLINE,      // ç¦»çº¿
};
// CPageGraph1 å¯¹è¯æ¡†
class CPageGraph1 : public CDialogEx
{
    DECLARE_DYNAMIC(CPageGraph1)
public:
    CPageGraph1(CWnd* pParent = nullptr);   // æ ‡å‡†æž„造函数
    virtual ~CPageGraph1();
public:
    void InitRxWindows();
    void UpdateDeviceStatus(int id, DeviceStatus status);
    void UpdateRobotPosition(float percentage);
    void RotateRobot(float angleInDegrees);
private:
    IObserver* m_pObserver;
    CServoGraph* m_pGraph;
    BOOL m_bIsRobotMoving;
    COLORREF m_crBkgnd;
    HBRUSH m_hbrBkgnd;
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_PAGE_GRAPH1 };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
    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 OnGraphItemClicked(NMHDR* pNMHDR, LRESULT* pResult);
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    afx_msg void OnTimer(UINT_PTR nIDEvent);
};
SourceCode/Bond/Servo/CPageGraph2.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,181 @@
// CPageGraph2.cpp: å®žçŽ°æ–‡ä»¶
//
#include "stdafx.h"
#include "Servo.h"
#include "CPageGraph2.h"
#include "afxdialogex.h"
// CPageGraph2 å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CPageGraph2, CDialogEx)
CPageGraph2::CPageGraph2(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_PAGE_GRAPH2, pParent)
{
    m_pEqsGraphWnd = nullptr;
    m_crBkgnd = PAGE_GRPAH2_BACKGROUND_COLOR;
    m_hbrBkgnd = nullptr;
}
CPageGraph2::~CPageGraph2()
{
}
void CPageGraph2::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CPageGraph2, CDialogEx)
    ON_WM_CTLCOLOR()
    ON_WM_DESTROY()
    ON_WM_SIZE()
    ON_WM_TIMER()
END_MESSAGE_MAP()
// CPageGraph2 æ¶ˆæ¯å¤„理程序
BOOL CPageGraph2::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    SetTimer(1, 2000, nullptr);
    // filter graph wnd
    EqsGraphListener listener;
    listener.onCheckConnectPin = [](PIN* pPin1, PIN* pPin2) -> bool {
        ASSERT(pPin1);
        ASSERT(pPin2);
        ASSERT(pPin1->pData);
        ASSERT(pPin2->pData);
        //int nRet = ((IPin*)pPin1->pData)->checkConnectPin((IPin*)pPin2->pData);
        //if (nRet >= 0) {
        //    return true;
        //}
        return false;
    };
    listener.onConnectPin = [](PIN* pPin1, PIN* pPin2) -> bool {
        ASSERT(pPin1);
        ASSERT(pPin2);
        ASSERT(pPin1->pData);
        ASSERT(pPin2->pData);
        //int nRet = ((IPin*)pPin1->pData)->connectPin((IPin*)pPin2->pData);
        //if (nRet >= 0) {
        //    return true;
        //}
        return false;
    };
    listener.onDisconnectPin = [](PIN* pPin) -> bool {
        ASSERT(pPin);
        ASSERT(pPin->pData);
        //int nRet = ((IPin*)pPin->pData)->disconnect();
        //if (nRet >= 0) {
        //    return true;
        //}
        return false;
    };
    listener.onDeleteEqItem = [&](EQITEM* pItem) -> bool {
        ASSERT(pItem);
        ASSERT(pItem->pData);
        return true;
        // return _filterManager.unload((CFilter*)pFilter->pData) >= 0;
    };
    listener.onEqItemPosChanged = [&](EQITEM* pItem, int x, int y) -> void {
        ASSERT(pItem);
    };
    listener.onDblckEqItem = [&](EQITEM* pItem) -> bool {
        ASSERT(pItem);
        return true;
    };
    listener.onRclickEqItem = [&](EQITEM* pItem) -> bool {
        ASSERT(pItem);
        return true;
    };
    m_pEqsGraphWnd = CEqsGraphWnd::FromHandle(GetDlgItem(IDC_EQSGRAPHWND1)->m_hWnd);
    m_pEqsGraphWnd->SetBkgndColor(m_crBkgnd);
    m_pEqsGraphWnd->SetOnListener(listener);
    return TRUE;  // return TRUE unless you set the focus to a control
                  // å¼‚常: OCX å±žæ€§é¡µåº”返回 FALSE
}
HBRUSH CPageGraph2::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
    if (nCtlColor == CTLCOLOR_STATIC) {
        pDC->SetBkColor(m_crBkgnd);
        pDC->SetTextColor(RGB(0, 0, 0));
    }
    if (m_hbrBkgnd == nullptr) {
        m_hbrBkgnd = CreateSolidBrush(m_crBkgnd);
    }
    return m_hbrBkgnd;
}
void CPageGraph2::OnDestroy()
{
    CDialogEx::OnDestroy();
    if (m_hbrBkgnd != nullptr) {
        ::DeleteObject(m_hbrBkgnd);
    }
}
void CPageGraph2::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);
    if (GetDlgItem(IDC_EQSGRAPHWND1) == nullptr) return;
    CRect rcClient;
    GetClientRect(&rcClient);
    GetDlgItem(IDC_EQSGRAPHWND1)->MoveWindow(0, 0, rcClient.Width(), rcClient.Height());
}
#define INPIN        1
#define OUTPIN        2
void CPageGraph2::AddEqToGraphWnd(SERVO::CEquipment* pEquipment)
{
    EQITEM* pItem = m_pEqsGraphWnd->AddItem(0, pEquipment->getName().c_str(), (DWORD_PTR)pEquipment);
    m_pEqsGraphWnd->SetItemType(pItem, ITEM_SMALL);
    std::vector<SERVO::CPin*>& inPins = pEquipment->getInputPins();
    for (auto inPin : inPins) {
        m_pEqsGraphWnd->AddPin(pItem, INPIN, inPin->getName().c_str(), (DWORD_PTR)inPin);
    }
    std::vector<SERVO::CPin*>& outPins = pEquipment->getOutputPins();
    for (auto outPin : outPins) {
        m_pEqsGraphWnd->AddPin(pItem, OUTPIN, outPin->getName().c_str(), (DWORD_PTR)outPin);
    }
}
void CPageGraph2::OnTimer(UINT_PTR nIDEvent)
{
    if (1 == nIDEvent) {
        KillTimer(1);
        std::list<SERVO::CEquipment*>& eqs = theApp.m_model.m_master.getEquipmentList();
        for (auto item : eqs) {
            AddEqToGraphWnd(item);
        }
    }
    CDialogEx::OnTimer(nIDEvent);
}
SourceCode/Bond/Servo/CPageGraph2.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
#pragma once
#include "EqsGraphWnd.h"
// CPageGraph2 å¯¹è¯æ¡†
class CPageGraph2 : public CDialogEx
{
    DECLARE_DYNAMIC(CPageGraph2)
public:
    CPageGraph2(CWnd* pParent = nullptr);   // æ ‡å‡†æž„造函数
    virtual ~CPageGraph2();
private:
    void AddEqToGraphWnd(SERVO::CEquipment* pEquipment);
private:
    CEqsGraphWnd* m_pEqsGraphWnd;
    COLORREF m_crBkgnd;
    HBRUSH m_hbrBkgnd;
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_PAGE_GRAPH2 };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
    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/CPanel.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
#include "stdafx.h"
#include "CPanel.h"
namespace SERVO {
    CPanel::CPanel()
    {
    }
    CPanel::~CPanel()
    {
    }
    std::string& CPanel::getClassName()
    {
        static std::string strName = "CPanel";
        return strName;
    }
    std::string CPanel::toString()
    {
        std::string strText;
        strText += "CPanel[";
        strText += ("ID:" + m_strID + ";");
        strText += "]";
        return strText;
    }
    void CPanel::setID(const char* pszID)
    {
        m_strID = pszID;
    }
    std::string& CPanel::getID()
    {
        return m_strID;
    }
}
SourceCode/Bond/Servo/CPanel.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
#pragma once
#include "Context.h"
#include <string>
namespace SERVO {
    class CPanel : public CContext
    {
    public:
        CPanel();
        virtual ~CPanel();
    public:
        virtual std::string& getClassName();
        virtual std::string toString();
        void setID(const char* pszID);
        std::string& getID();
    private:
        std::string m_strID;
    };
}
SourceCode/Bond/Servo/CPin.cpp
@@ -115,16 +115,15 @@
    int CPin::sendIntent(CIntent* pIntent)
    {
        if (m_pConnectedPin != NULL) {
            m_pConnectedPin->recvIntent(pIntent);
            return m_pConnectedPin->recvIntent(pIntent);
        }
        return 0;
        return FLOW_REJECT;
    }
    int CPin::recvIntent(CIntent* pIntent)
    {
        assert(m_pEquipment);
        m_pEquipment->recvIntent(this, pIntent);
        return 0;
        return m_pEquipment->recvIntent(this, pIntent);
    }
}
SourceCode/Bond/Servo/ColorTransfer.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
#include "stdafx.h"
#include "ColorTransfer.h"
CColorTransfer::CColorTransfer()
{
}
CColorTransfer::~CColorTransfer()
{
}
COLORREF CColorTransfer::ApproximateColor(COLORREF crScr, double diff)
{
    double h, s, l;
    RGB2HSL(crScr, h, s, l);
    l -= diff;
    return HSL2RGB(h, s, l);
}
void CColorTransfer::RGB2HSL(COLORREF color, double &H, double &S, double &L)
{
    double R, G, B, Max, Min, del_R, del_G, del_B, del_Max;
    R = GetRValue(color) / 255.0;       //Where RGB values = 0 Ã· 255
    G = GetGValue(color) / 255.0;
    B = GetBValue(color) / 255.0;
    Min = min(R, min(G, B));    //Min. value of RGB
    Max = max(R, max(G, B));    //Max. value of RGB
    del_Max = Max - Min;        //Delta RGB value
    L = (Max + Min) / 2.0;
    if (del_Max == 0) {
        // This is a gray, no chroma...
        // H = 2.0/3.0;          //Windows下S值为0时,H值始终为160(2/3*240)
        H = 0;                  //HSL results = 0 Ã· 1
        S = 0;
    }
    else {
        //Chromatic data...
        if (L < 0.5) {
            S = del_Max / (Max + Min);
        }
        else {
            S = del_Max / (2 - Max - Min);
        }
        del_R = (((Max - R) / 6.0) + (del_Max / 2.0)) / del_Max;
        del_G = (((Max - G) / 6.0) + (del_Max / 2.0)) / del_Max;
        del_B = (((Max - B) / 6.0) + (del_Max / 2.0)) / del_Max;
        if (R == Max) {
            H = del_B - del_G;
        }
        else if (G == Max) {
            H = (1.0 / 3.0) + del_R - del_B;
        }
        else if (B == Max) {
            H = (2.0 / 3.0) + del_G - del_R;
        }
        if (H < 0) {
            H += 1;
        }
        if (H > 1) {
            H -= 1;
        }
    }
}
COLORREF CColorTransfer::HSL2RGB(double H, double S, double L)
{
    double R, G, B;
    double var_1, var_2;
    if (S == 0) {
        // HSL values = 0 Ã· 1
        R = L * 255.0;                   //RGB results = 0 Ã· 255
        G = L * 255.0;
        B = L * 255.0;
    }
    else {
        if (L < 0.5) {
            var_2 = L * (1 + S);
        }
        else {
            var_2 = (L + S) - (S * L);
        }
        var_1 = 2.0 * L - var_2;
        R = 255.0 * Hue2RGB(var_1, var_2, H + (1.0 / 3.0));
        G = 255.0 * Hue2RGB(var_1, var_2, H);
        B = 255.0 * Hue2RGB(var_1, var_2, H - (1.0 / 3.0));
    }
    return RGB(R, G, B);
}
double CColorTransfer::Hue2RGB(double v1, double v2, double vH)
{
    if (vH < 0) {
        vH += 1;
    }
    if (vH > 1) {
        vH -= 1;
    }
    if (6.0 * vH < 1) {
        return v1 + (v2 - v1) * 6.0 * vH;
    }
    if (2.0 * vH < 1) {
        return v2;
    }
    if (3.0 * vH < 2) {
        return v1 + (v2 - v1) * ((2.0 / 3.0) - vH) * 6.0;
    }
    return (v1);
}
SourceCode/Bond/Servo/ColorTransfer.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
#pragma once
class CColorTransfer
{
public:
    CColorTransfer();
    ~CColorTransfer();
public:
    static void RGB2HSL(COLORREF color, double &H, double &S, double &L);
    static COLORREF HSL2RGB(double H, double S, double L);
    static COLORREF ApproximateColor(COLORREF crScr, double diff);
private:
    static double Hue2RGB(double v1, double v2, double vH);
};
SourceCode/Bond/Servo/Common.h
@@ -25,6 +25,8 @@
#define PANEL_MASTER_BACKGROUND_COLOR        RGB(255, 255, 255)
#define PANEL_ATTRIBUTES_BACKGROUND_COLOR    RGB(255, 255, 255)
#define PANEL_EQUIPMENT_BACKGROUND_COLOR    RGB(255, 255, 255)
#define PAGE_GRPAH1_BACKGROUND_COLOR        RGB(255, 255, 255)
#define PAGE_GRPAH2_BACKGROUND_COLOR        RGB(255, 255, 255)
#define EQ_BOX_OFFLINE                        RGB(222, 222, 222)
#define EQ_BOX_ONLINE                        RGB(0, 176, 80)
#define EQ_BOX_FRAME1                        RGB(22, 22, 22)
@@ -75,6 +77,7 @@
#define STEP_EQ_VCR_ENABLE        _T("EQVCREnable")
/* base alarm */
#define BASE_ALARM_EFEM        10000
#define BASE_ALARM_BONDER1    20000
#define BASE_ALARM_BONDER2    30000
@@ -88,3 +91,12 @@
/* è‡ªå®šä¹‰æ¶ˆæ¯ */
#define ID_MSG_PANEL_RESIZE            WM_USER + 1998
/* æµç¨‹æŽ§åˆ¶ç›¸å…³ä»£ç  */
#define FLOW_REJECT                    0x0
#define FLOW_ACCEPT                    0x1
#define FLOW_TEST                    0x1000
#define FLOW_SIGNAL                    0x1001
#define FLOW_DATA                    0x1002
#define FLOW_MOVE_MATERIAL            0x1003
SourceCode/Bond/Servo/Context.cpp
@@ -5,7 +5,7 @@
CContext::CContext()
{
    m_nRef = 0;
    m_nRetCode = CRC_UNKNOWN;
    m_nRetCode = 0;
    m_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    InitializeCriticalSection(&m_criticalSection);
}
SourceCode/Bond/Servo/EqsGraphWnd.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2377 @@
#include "stdafx.h"
#include "EqsGraphWnd.h"
#include "ColorTransfer.h"
#include "MapPosWnd.h"
#define INPIN        1
#define OUTPIN        2
#define ITEM_CX_SMALL    150
#define ITEM_CY_SMALL    90
#define ITEM_CX_NORMAL    250
#define ITEM_CY_NORMAL    150
#define ITEM_CX_LARGE    400
#define ITEM_CY_LARGE    240
#define HT_NOWHERE        0x1
#define HT_ITEM            0x2
#define HT_PIN            0x4
#define HT_LINE            0x8
#define PINWIDTH        8
#define PINHEIGHT        12
#define TIMER_FLASH                1
#define TIMER_ANIMATION_RECT    2
#define MAPPOSSIZE                    150
#define MAPPOSWND_PADDING_RIGHT        12
#define MAPPOSWND_ID                1001
CEqsGraphWnd::CEqsGraphWnd()
{
    m_bUseGdiPlus = TRUE;
    m_hWnd = NULL;
    m_crFrame = GetSysColor(COLOR_WINDOWFRAME);
    m_crBkgnd = RGB(255, 255, 255);
    m_listener.onConnectPin = nullptr;
    m_listener.onCheckConnectPin = nullptr;
    m_listener.onDisconnectPin = nullptr;
    m_listener.onDeleteEqItem = nullptr;
    m_listener.onEqItemPosChanged = nullptr;
    m_listener.onDblckEqItem = nullptr;
    m_listener.onRclickEqItem = nullptr;
    m_crItemBackground[0] = RGB(218, 218, 218);
    m_crItemBackground[1] = RGB(193, 208, 227);
    m_crItemFrame[0] = RGB(128, 128, 128);
    m_crItemFrame[1] = RGB(147, 172, 206);
    m_crItemNameText[0] = RGB(0, 0, 0);
    m_crItemNameText[1] = RGB(0, 0, 0);
    m_crItemIdText[0] = CColorTransfer::ApproximateColor(m_crItemNameText[0], -0.3f);
    m_crItemIdText[1] = m_crItemIdText[0];
    m_nCurSel = -1;
    m_bMultiSelect = FALSE;
    m_nItemRound = 0;
    m_pCurItem = NULL;
    m_pCurPin = NULL;
    m_pSelLineOutPin = NULL;
    m_crPinBkgnd[0] = RGB(218, 218, 218);
    m_crPinBkgnd[1] = RGB(193, 0, 0);
    m_crPinBkgnd[2] = RGB(193, 0, 0);
    m_nStageCx = 4000;
    m_nStageCy = 3000;
    m_nOffsetX = 0;
    m_nOffsetY = 0;
    m_pFlashItem = NULL;
    m_nFlashCount = 0;
    m_hWndMapPos = NULL;
    m_bEnableScroll = FALSE;
    m_nMagneticLinHoz = 0;
    m_nMagneticLinVer = 0;
    m_hFontTitle = nullptr;
}
CEqsGraphWnd::~CEqsGraphWnd()
{
    ReleaseAllItems();
}
BOOL CEqsGraphWnd::RegisterWndClass()
{
    WNDCLASS wc;
    wc.lpszClassName = EQSGRAPHWND_CLASS;
    wc.hInstance = AfxGetInstanceHandle();
    wc.lpfnWndProc = WindowProc;
    wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = 0;
    wc.lpszMenuName = NULL;
    wc.hbrBackground = NULL;
    wc.style = CS_GLOBALCLASS | CS_DBLCLKS;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    // æ³¨å†Œçª—口类
    return (::RegisterClass(&wc) != 0);
}
CEqsGraphWnd* CEqsGraphWnd::FromHandle(HWND hWnd)
{
    CEqsGraphWnd* pEqsGraphWnd = (CEqsGraphWnd*)::GetProp(hWnd, EQSGRAPHWND_TAG);
    return pEqsGraphWnd;
}
CEqsGraphWnd* CEqsGraphWnd::Hook(HWND hWnd)
{
    CEqsGraphWnd* pEqsGraphWnd = (CEqsGraphWnd*)GetProp(hWnd, EQSGRAPHWND_TAG);
    if (pEqsGraphWnd == NULL) {
        pEqsGraphWnd = new CEqsGraphWnd();
        pEqsGraphWnd->m_hWnd = hWnd;
        SetProp(hWnd, EQSGRAPHWND_TAG, (HANDLE)pEqsGraphWnd);
    }
    return pEqsGraphWnd;
}
void CEqsGraphWnd::InitFont()
{
    HDC hDC = GetDC(NULL);
    HFONT hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
    {
        LOGFONT lf = { 0 };
        ::GetObject(hFont, sizeof(LOGFONT), &lf);
        m_hFontName = CreateFontIndirect(&lf);
    }
    {
        LOGFONT lf = { 0 };
        ::GetObject(hFont, sizeof(LOGFONT), &lf);
        int nSize = int(-lf.lfHeight * 72.0 / GetDeviceCaps(hDC, LOGPIXELSY) + 0.5);
        lf.lfHeight = MulDiv(0 - (nSize - 2), GetDeviceCaps(hDC, LOGPIXELSX), 72);
        m_hFontId = CreateFontIndirect(&lf);
    }
    ::ReleaseDC(NULL, hDC);
}
void CEqsGraphWnd::SetItemRound(int nRound)
{
    m_nItemRound = nRound;
}
void CEqsGraphWnd::SetDefaultItemBackgroundColor(COLORREF crNormal, COLORREF crSel)
{
    m_crItemBackground[0] = crNormal;
    m_crItemBackground[1] = crSel;
}
void CEqsGraphWnd::SetDefaultItemFrameColor(COLORREF crNormal, COLORREF crSel)
{
    m_crItemFrame[0] = crNormal;
    m_crItemFrame[1] = crSel;
}
void CEqsGraphWnd::SetDefaultItemTextColor(COLORREF crNormal, COLORREF crSel)
{
    m_crItemNameText[0] = crNormal;
    m_crItemNameText[1] = crSel;
    m_crItemIdText[0] = CColorTransfer::ApproximateColor(m_crItemNameText[0], -0.3f);
    m_crItemIdText[1] = CColorTransfer::ApproximateColor(m_crItemNameText[1], -0.3f);
}
void CEqsGraphWnd::EnableScroll(BOOL bEnable)
{
    m_bEnableScroll = bEnable;
}
void CEqsGraphWnd::EnableMultiSelect()
{
    m_bMultiSelect = TRUE;
}
void CEqsGraphWnd::Init()
{
    InitFont();
    CalculateScollbar();
    long style = GetWindowLong(m_hWnd, GWL_STYLE);
    SetWindowLong(m_hWnd, GWL_STYLE, style | WS_CLIPCHILDREN);
    // MapPosWnd
    if (m_hWndMapPos == NULL) {
        m_hWndMapPos = CreateWindowEx(0, MAPPOSWND_CLASS,
            NULL, WS_CHILD | WS_VISIBLE,
            0, 0, 400, 400,
            m_hWnd, (HMENU)MAPPOSWND_ID, NULL, NULL);
        long styleex = GetWindowLong(m_hWndMapPos, GWL_EXSTYLE);
        SetWindowLong(m_hWndMapPos, GWL_EXSTYLE, styleex | WS_EX_CLIENTEDGE);
        CMapPosWnd *pMapPosWnd = CMapPosWnd::FromHandle(m_hWndMapPos);
        pMapPosWnd->SetWndMaxSize(MAPPOSSIZE);
        pMapPosWnd->SetStageSize(m_nStageCx, m_nStageCy, TRUE);
    }
}
void CEqsGraphWnd::CalculateMapPos()
{
    CRect rcClient;
    GetClientRect(m_hWnd, &rcClient);
    ::OffsetRect(&rcClient, m_nOffsetX, m_nOffsetY);
    CMapPosWnd *pMapPosWnd = CMapPosWnd::FromHandle(m_hWndMapPos);
    pMapPosWnd->SetViewPort(&rcClient, TRUE);
}
void CEqsGraphWnd::CalculateScollbar()
{
    RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
    // vert scroll
    if(m_bEnableScroll) {
        SCROLLINFO scrinffo;
        scrinffo.cbSize = sizeof(SCROLLINFO);
        scrinffo.fMask = SIF_ALL;
        scrinffo.nMax = m_nStageCy;
        scrinffo.nMin = 0;
        scrinffo.nPos = m_nOffsetY;
        scrinffo.nTrackPos = 0;
        scrinffo.nPage = rcClient.bottom - rcClient.top;
        SetScrollInfo(m_hWnd, SB_VERT, &scrinffo, TRUE);
    }
    // horz scroll
    if (m_bEnableScroll) {
        SCROLLINFO scrinffo;
        scrinffo.cbSize = sizeof(SCROLLINFO);
        scrinffo.fMask = SIF_ALL;
        scrinffo.nMax = m_nStageCx;
        scrinffo.nMin = 0;
        scrinffo.nPos = m_nOffsetX;
        scrinffo.nTrackPos = 0;
        scrinffo.nPage = rcClient.right - rcClient.left;
        SetScrollInfo(m_hWnd, SB_HORZ, &scrinffo, TRUE);
    }
}
/*
 * è®¡ç®—磁力线位置
 */
void CEqsGraphWnd::CalculateMagneticLine(EQITEM* pItem, LPRECT lprcItemRect, int &hoz, int &ver)
{
    hoz = 0;
    ver = 0;
#define MAGNETIC_DIS        10
    // æ£€æµ‹æ˜¯å¦æŽ¥è¿‘或对齐
    for (int i = 0; i < m_arItem.GetSize(); i++) {
        EQITEM *pTemp = (EQITEM*)m_arItem.GetAt(i);
        if (pTemp != pItem) {
            if (abs(lprcItemRect->left - pTemp->rect.left) < MAGNETIC_DIS) {
                ver = pTemp->rect.left;
                break;
            }
            else if (abs(lprcItemRect->right - pTemp->rect.right) < MAGNETIC_DIS) {
                ver = pTemp->rect.right - (lprcItemRect->right- lprcItemRect->left);
                break;
            }
        }
    }
    for (int i = 0; i < m_arItem.GetSize(); i++) {
        EQITEM* pTemp = (EQITEM*)m_arItem.GetAt(i);
        if (pTemp != pItem) {
            if (abs(lprcItemRect->top - pTemp->rect.top) < MAGNETIC_DIS) {
                hoz = pTemp->rect.top;
                break;
            }
            else if (abs(lprcItemRect->bottom - pTemp->rect.bottom) < MAGNETIC_DIS) {
                hoz = pTemp->rect.bottom - (lprcItemRect->bottom - lprcItemRect->top);
                break;
            }
        }
    }
}
void CEqsGraphWnd::Release()
{
    ::DeleteObject(m_hFontName);
    ::DeleteObject(m_hFontId);
    if (m_hFontTitle != nullptr) {
        ::DeleteObject(m_hFontTitle);
    }
    // delete
    delete this;
}
/*
 * å–å¾—In Pin的区域
 * pItem -- EQITEM
 * lpRect -- å¾—到的Rect
 * è¿”回是否成功
 */
BOOL CEqsGraphWnd::GetItemRect(EQITEM* pItem, LPRECT lpRect)
{
    ASSERT(pItem);
    if (pItem == m_pAnimationItem) {
        lpRect->left = (int)(m_rcAnimation.left - m_nOffsetX);
        lpRect->top = (int)(m_rcAnimation.top - m_nOffsetY);
        lpRect->right = (int)(m_rcAnimation.right - m_nOffsetX);
        lpRect->bottom = (int)(m_rcAnimation.bottom - m_nOffsetY);
    }
    else {
        lpRect->left = (int)(pItem->rect.left - m_nOffsetX);
        lpRect->top = (int)(pItem->rect.top - m_nOffsetY);
        lpRect->right = (int)(pItem->rect.right - m_nOffsetX);
        lpRect->bottom = (int)(pItem->rect.bottom - m_nOffsetY);
    }
    return TRUE;
}
BOOL CEqsGraphWnd::GetItemWarperRect(EQITEM* pItem, LPRECT lpRect)
{
    CopyRect(lpRect, &pItem->rect);
    lpRect->left -= PINWIDTH;
    lpRect->right += PINWIDTH;
    return TRUE;
}
/*
 * å–å¾—In Pin的区域
 * pItem -- EQITEM
 * nPinIndex -- in pin索引
 * lpRect -- å¾—到的Rect
 * è¿”回是否成功
 */
BOOL CEqsGraphWnd::GetInPinRect(EQITEM* pItem, int nPinIndex, LPRECT lpRect)
{
    CPtrArray * pPins = (CPtrArray *)pItem->pInPins;
    if (nPinIndex >= pPins->GetSize()) {
        return FALSE;
    }
    int nBottomMargin = pPins->GetCount() >= 4 ? 8 : 0;
    int nSpace = ((pItem->rect.bottom - nBottomMargin - pItem->rect.top) - (int)pPins->GetSize() * PINHEIGHT) / (pPins->GetSize() + 1);
    lpRect->right = pItem->rect.left+1 - m_nOffsetX;
    lpRect->left = lpRect->right - PINWIDTH;
    lpRect->bottom = pItem->rect.top + (nSpace + PINHEIGHT) * (nPinIndex+1) - m_nOffsetY;
    lpRect->top = lpRect->bottom - PINHEIGHT;
    return TRUE;
}
/*
 * å–å¾—Out Pin的区域
 * pItem -- EQITEM
 * nPinIndex -- in pin索引
 * lpRect -- å¾—到的Rect
 * è¿”回是否成功
 */
BOOL CEqsGraphWnd::GetOutPinRect(EQITEM* pItem, int nPinIndex, LPRECT lpRect)
{
    CPtrArray * pPins = (CPtrArray *)pItem->pOutPins;
    if (nPinIndex >= pPins->GetSize()) {
        return FALSE;
    }
    int nSpace = ((pItem->rect.bottom - pItem->rect.top) - (int)pPins->GetSize() * PINHEIGHT) / (pPins->GetSize() + 1);
    lpRect->left = pItem->rect.right-1 - m_nOffsetX;
    lpRect->right = lpRect->left + PINWIDTH;
    lpRect->bottom = pItem->rect.top + (nSpace + PINHEIGHT) * (nPinIndex + 1) - m_nOffsetY;
    lpRect->top = lpRect->bottom - PINHEIGHT;
    return TRUE;
}
/*
 * å–å¾—Pin的Point
 * pItem -- EQITEM
 * nPinIndex -- in pin索引
 * lpRect -- å¾—到的Rect
 * è¿”回是否成功
 */
BOOL CEqsGraphWnd::GetPinPoint(PIN *pPin, LPPOINT lpPoint)
{
    ASSERT(pPin);
    ASSERT(pPin->pItem);
    CPtrArray * pPins;
    RECT rcPin;
    // in pin?
    pPins = (CPtrArray *)pPin->pItem->pInPins;
    for(int i=0; i<pPins->GetCount(); i++) {
        if (pPins->GetAt(i) == pPin) {
            if (GetInPinRect(pPin->pItem, i, &rcPin)) {
                lpPoint->x = rcPin.left + (rcPin.right - rcPin.left) / 2;
                lpPoint->y = rcPin.top + (rcPin.bottom - rcPin.top) / 2;
                return TRUE;
            }
        }
    }
    // out pin?
    pPins = (CPtrArray *)pPin->pItem->pOutPins;
    for (int i = 0; i<pPins->GetCount(); i++) {
        if (pPins->GetAt(i) == pPin) {
            if (GetOutPinRect(pPin->pItem, i, &rcPin)) {
                lpPoint->x = rcPin.left + (rcPin.right - rcPin.left) / 2;
                lpPoint->y = rcPin.top + (rcPin.bottom - rcPin.top) / 2;
                return TRUE;
            }
        }
    }
    return FALSE;
}
void CEqsGraphWnd::ReleaseItem(EQITEM* pItem)
{
    ASSERT(pItem);
    CPtrArray *pArray = (CPtrArray *)pItem->pInPins;
    for (int j = 0; j < pArray->GetSize(); j++) {
        PIN *pPin = (PIN *)pArray->GetAt(j);
        if (pPin->pConnectedPin != NULL) {
            pPin->pConnectedPin->pConnectedPin = NULL;
        }
        delete pPin;
    }
    delete pArray;
    pArray = (CPtrArray *)pItem->pOutPins;
    for (int j = 0; j < pArray->GetSize(); j++) {
        PIN *pPin = (PIN *)pArray->GetAt(j);
        if (pPin->pConnectedPin != NULL) {
            pPin->pConnectedPin->pConnectedPin = NULL;
        }
        delete pPin;
    }
    delete pArray;
    delete pItem;
}
void CEqsGraphWnd::ReleaseAllItems()
{
    for (int i = 0; i < m_arItem.GetCount(); i++) {
        ReleaseItem((EQITEM*)m_arItem.GetAt(i));
    }
    m_arItem.RemoveAll();
}
int CEqsGraphWnd::GetPinState(PIN *pPin)
{
    if (pPin == m_pCurPin) {
        return 1;
    }
    return 0;
}
/*
 * æ¸…空PIN连接线缓存点,以便重新计算和绘制
 */
void CEqsGraphWnd::ClearConnectedLinePoint(EQITEM*& pItem)
{
    ASSERT(pItem);
    CPtrArray *pPins;
    PIN *pPin;
    pPins = (CPtrArray *)pItem->pInPins;
    for (int j = 0; j < pPins->GetSize(); j++) {
        pPin = (PIN *)pPins->GetAt(j);
        if (pPin->pConnectedPin != NULL) {
            pPin->pConnectedPin->nLinePtCount = 0;
        }
    }
    pPins = (CPtrArray *)pItem->pOutPins;
    for (int j = 0; j < pPins->GetSize(); j++) {
        pPin = (PIN *)pPins->GetAt(j);
        pPin->nLinePtCount = 0;
    }
}
void CEqsGraphWnd::SetOnListener(EqsGraphListener& listener)
{
    m_listener.onConnectPin = listener.onConnectPin;
    m_listener.onCheckConnectPin = listener.onCheckConnectPin;
    m_listener.onDisconnectPin = listener.onDisconnectPin;
    m_listener.onDeleteEqItem = listener.onDeleteEqItem;
    m_listener.onEqItemPosChanged = listener.onEqItemPosChanged;
    m_listener.onDblckEqItem = listener.onDblckEqItem;
    m_listener.onRclickEqItem = listener.onRclickEqItem;
}
BOOL CEqsGraphWnd::SetCurSel(int nSel)
{
    if (!(nSel == -1 || nSel < m_arItem.GetCount())) {
        return FALSE;
    }
    m_nCurSel = nSel;
    RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    return TRUE;
}
BOOL CEqsGraphWnd::SetCurSel(CString strItemName)
{
    int nIndex = -1;
    for (int i = 0; i < m_arItem.GetCount(); i++) {
        EQITEM* pItem = (EQITEM*)m_arItem.GetAt(i);
        if (strItemName.Compare(pItem->text) == 0) {
            nIndex = i;
            break;
        }
    }
    if (nIndex == -1) {
        return FALSE;
    }
    m_nCurSel = nIndex;
    RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    return TRUE;
}
BOOL CEqsGraphWnd::SetCurSel(DWORD_PTR pData)
{
    int nIndex = -1;
    for (int i = 0; i < m_arItem.GetCount(); i++) {
        EQITEM* pItem = (EQITEM*)m_arItem.GetAt(i);
        if (pItem->pData == pData) {
            nIndex = i;
            break;
        }
    }
    if (nIndex == -1) {
        return FALSE;
    }
    m_nCurSel = nIndex;
    RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    return TRUE;
}
/*
 * nType: ITEM_SMALL, ITEM_NORMAL or ITEM_LARGE
 */
EQITEM* CEqsGraphWnd::AddItem(int id, CString strText, DWORD_PTR dwData, int nType/* = ITEM_NORMAL*/)
{
    // éœ€è¦è®¡ç®—一个新位置,不然全部重叠在一起
    int x, y;
    x = (m_arItem.GetCount() % 4) * 218;
    y = (m_arItem.GetCount() / 4) * 168;
    EQITEM* pItem = new EQITEM;
    memset(pItem, 0, sizeof(EQITEM));
    pItem->id = id;
    pItem->nShowType = nType;
    pItem->rect.left = x + 20 + m_nOffsetX;
    pItem->rect.top = y + 50 + m_nOffsetY;
    if (pItem->nShowType == ITEM_SMALL) {
        pItem->rect.right = pItem->rect.left + ITEM_CX_SMALL;
        pItem->rect.bottom = pItem->rect.top + ITEM_CY_SMALL;
    }
    else if(pItem->nShowType == ITEM_LARGE){
        pItem->rect.right = pItem->rect.left + ITEM_CX_LARGE;
        pItem->rect.bottom = pItem->rect.top + ITEM_CY_LARGE;
    }
    else {
        pItem->rect.right = pItem->rect.left + ITEM_CX_NORMAL;
        pItem->rect.bottom = pItem->rect.top + ITEM_CY_NORMAL;
    }
    pItem->pData = dwData;
    pItem->pInPins = (DWORD_PTR)new CPtrArray();
    pItem->pOutPins = (DWORD_PTR)new CPtrArray();
    int len = min(63, strText.GetLength());
    memcpy(pItem->text, (LPTSTR)(LPCTSTR)strText, len);
    pItem->text[len] = '\0';
    m_arItem.Add(pItem);
    RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    return pItem;
}
void CEqsGraphWnd::RemoveItem(EQITEM* pItem)
{
    BOOL bChanged = FALSE;
    if (m_listener.onDeleteEqItem != NULL) {
        if (m_listener.onDeleteEqItem(m_pCurItem)) {
            bChanged = DeleteItem(m_pCurItem) >= 0;
        }
    }
    if (bChanged) {
        if (pItem == m_pCurItem) {
            m_pCurItem = NULL;
        }
        RECT rcClient;
        GetClientRect(m_hWnd, &rcClient);
        ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    }
}
void CEqsGraphWnd::SetItemText(EQITEM* pItem, CString strText)
{
    int len = min(63, strText.GetLength());
    memcpy(pItem->text, (LPTSTR)(LPCTSTR)strText, len);
    pItem->text[len] = '\0';
    ::InvalidateRect(m_hWnd, &pItem->rect, TRUE);
}
void CEqsGraphWnd::SetItemType(EQITEM* pItem, int nType)
{
    pItem->nShowType = nType;
    if (pItem->nShowType == ITEM_SMALL) {
        pItem->rect.right = pItem->rect.left + ITEM_CX_SMALL;
        pItem->rect.bottom = pItem->rect.top + ITEM_CY_SMALL;
    }
    else if (pItem->nShowType == ITEM_LARGE) {
        pItem->rect.right = pItem->rect.left + ITEM_CX_LARGE;
        pItem->rect.bottom = pItem->rect.top + ITEM_CY_LARGE;
    }
    else {
        pItem->rect.right = pItem->rect.left + ITEM_CX_NORMAL;
        pItem->rect.bottom = pItem->rect.top + ITEM_CY_NORMAL;
    }
    ::InvalidateRect(m_hWnd, &pItem->rect, TRUE);
}
PIN * CEqsGraphWnd::AddPin(EQITEM* pItem, int nType, CString strName, DWORD_PTR dwData)
{
    ASSERT(pItem);
    ASSERT(nType == INPIN || nType == OUTPIN);
    ASSERT(pItem->pInPins);
    ASSERT(pItem->pOutPins);
    PIN *pPin = new PIN;
    memset(pPin, 0, sizeof(PIN));
    pPin->pItem = pItem;
    pPin->nIndex = nType == INPIN ? ((CPtrArray *)pItem->pInPins)->GetSize() : ((CPtrArray *)pItem->pOutPins)->GetSize();
    pPin->nType = nType;
    pPin->pData = dwData;
    int len = MIN(sizeof(pPin->text), strName.GetLength());
    memcpy(pPin->text, (LPTSTR)(LPCTSTR)strName, len);
    pPin->text[len] = '\0';
    CPtrArray *pArray = NULL;
    if (nType == INPIN) {
        pArray = (CPtrArray *)pItem->pInPins;
    }
    else {
        pArray = (CPtrArray *)pItem->pOutPins;
    }
    ASSERT(pItem->pOutPins);
    pArray->Add(pPin);
    return pPin;
}
EQITEM* CEqsGraphWnd::GetItem(DWORD_PTR dwData)
{
    for (int i = 0; i < m_arItem.GetCount(); i++) {
        EQITEM* pItem = (EQITEM*)m_arItem.GetAt(i);
        if (pItem->pData == dwData) {
            return pItem;
        }
    }
    return NULL;
}
PIN * CEqsGraphWnd::GetPin(DWORD_PTR dwItemData, DWORD_PTR dwPinData)
{
    EQITEM* pItem = GetItem(dwItemData);
    if (pItem != NULL) {
        CPtrArray *pArray = (CPtrArray *)pItem->pInPins;
        for (int i = 0; i < pArray->GetCount(); i++) {
            PIN *pPin = (PIN *)pArray->GetAt(i);
            if (pPin->pData == dwPinData) {
                return pPin;
            }
        }
        pArray = (CPtrArray *)pItem->pOutPins;
        for (int i = 0; i < pArray->GetCount(); i++) {
            PIN *pPin = (PIN *)pArray->GetAt(i);
            if (pPin->pData == dwPinData) {
                return pPin;
            }
        }
    }
    return NULL;
}
int CEqsGraphWnd::ConnectPin(DWORD_PTR dwItem1Data, DWORD_PTR dwPin1Data, DWORD_PTR dwItem2Data, DWORD_PTR dwPin2Data)
{
    PIN *pPin1, *pPin2;
    pPin1 = GetPin(dwItem1Data, dwPin1Data);
    if (pPin1 == NULL) {
        return -1;
    }
    pPin2 = GetPin(dwItem2Data, dwPin2Data);
    if (pPin2 == NULL) {
        return -2;
    }
    pPin1->pConnectedPin = pPin2;
    pPin2->pConnectedPin = pPin1;
    return 0;
}
// åˆ é™¤Item, å¦‚æžœpin有连接,注意先断开
int CEqsGraphWnd::DeleteItem(EQITEM* pItem)
{
    for (int i = 0; i < m_arItem.GetSize(); i++) {
        if (pItem == (EQITEM*)m_arItem.GetAt(i)) {
            m_arItem.RemoveAt(i);
            ReleaseItem(pItem);
            return 0;
        }
    }
    return -1;
}
void CEqsGraphWnd::DeleteAllItems()
{
    ReleaseAllItems();
    RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
}
void CEqsGraphWnd::SetCurrentItem(EQITEM* pItem)
{
    if (m_pCurItem != NULL) {
        m_pCurItem->bHighlight = FALSE;
    }
    m_pCurItem = pItem;
    if (m_pCurItem != NULL) {
        m_pCurItem->bHighlight = TRUE;
    }
    RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
}
/*
 * è®¾ç½®å­é¡¹çš„选中状态
 */
void CEqsGraphWnd::SetItemSelectState(int nIndex, BOOL bSelect)
{
    if (nIndex >= m_arItem.GetCount()) {
        return;
    }
    EQITEM *pItem = (EQITEM*)m_arItem.GetAt(nIndex);
    pItem->bHighlight = bSelect;
    RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
}
void CEqsGraphWnd::Notify(int nCode, int dwData, int dwData1/* = 0*/, int dwData2/* = 0*/)
{
    HWND hParent;
    hParent = GetParent(m_hWnd);
    if (hParent != NULL) {
        EQSGRAPHWND_NMHDR nmhdr;
        nmhdr.nmhdr.hwndFrom = m_hWnd;
        nmhdr.nmhdr.idFrom = GetWindowLong(m_hWnd, GWL_ID);
        nmhdr.nmhdr.code = nCode;
        nmhdr.dwData = dwData;
        nmhdr.dwData1 = dwData1;
        nmhdr.dwData2 = dwData2;
        SendMessage(hParent, WM_NOTIFY, (WPARAM)nmhdr.nmhdr.idFrom, (LPARAM)&nmhdr);
    }
}
/*
 * æ£€æµ‹åæ ‡ç‚¹æ‰€åœ¨çš„项
 * è¿”回项类型, å¦‚HT_ITEM, HT_PIN, HT_LINE
 * pItem - æ‰€åœ¨çš„EQITEM
 * pPin --所在的pin, å¦‚果在连线上,表示所属pin, out pin;
 */
int CEqsGraphWnd::HighTest(POINT pt, OUT EQITEM*& pItem, OUT PIN *& pPin)
{
    // æ£€æµ‹æ˜¯å¦åœ¨æŸä¸ªå­é¡¹
    int nRet = HT_NOWHERE;
    pItem = NULL;
    pPin = NULL;
    RECT rcItem;
    for (int i = m_arItem.GetSize() - 1; i >= 0 ; i--) {
        EQITEM *pTempItem = (EQITEM*)m_arItem.GetAt(i);
        GetItemRect(pTempItem, &rcItem);
        if (::PtInRect(&rcItem, pt)) {
            // åœ¨Item
            pItem = pTempItem;
            nRet = HT_ITEM;
            break;
        }
        else {
            RECT rcPin;
            CPtrArray * pPins = (CPtrArray *)pTempItem->pInPins;
            for (int j = 0; j < pPins->GetSize(); j++) {
                if (GetInPinRect(pTempItem, j, &rcPin) && ::PtInRect(&rcPin, pt)) {
                    // åœ¨in pin上
                    pPin = (PIN *)pPins->GetAt(j);
                    pItem = pTempItem;
                    nRet = HT_PIN;
                    break;
                }
            }
            if (nRet == HT_NOWHERE) {
                pPins = (CPtrArray *)pTempItem->pOutPins;
                for (int j = 0; j < pPins->GetSize(); j++) {
                    if (GetOutPinRect(pTempItem, j, &rcPin) && ::PtInRect(&rcPin, pt)) {
                        // åœ¨out pin
                        pPin = (PIN *)pPins->GetAt(j);
                        pItem = pTempItem;
                        nRet = HT_PIN;
                        break;
                    }
                    else {
                        // æ˜¯å¦åœ¨pin连接线上,即判断点是否在线上
                        // ç‚¹åˆ°ç›´çº¿çš„距离公式(先通过p1,p2用两点式求出直线的表达式,再套距离公式);abs()为取绝对值函数,sqrt()为开根号函数
                        PIN *pTempPin = (PIN *)pPins->GetAt(j);
                        if (pTempPin->pConnectedPin != NULL && pTempPin->nLinePtCount > 1) {
                            for (int i = 0; i < pTempPin->nLinePtCount - 1; i++) {
                                double distance = PointToSegDist(pt.x + m_nOffsetX, pt.y + m_nOffsetY,
                                    pTempPin->ptConnectedLine[i].x, pTempPin->ptConnectedLine[i].y,
                                    pTempPin->ptConnectedLine[i+1].x, pTempPin->ptConnectedLine[i+1].y);
                                if (distance < 5.0) {
                                    nRet = HT_LINE;
                                    pPin = pTempPin;
                                    break;
                                }
                            }
                            if (nRet == HT_LINE) {
                                break;
                            }
                        }
                    }
                }
            }
            if (nRet != HT_NOWHERE) {
                break;
            }
        }
    }
    return nRet;
}
/*
 * ç»˜åˆ¶è™šçº¿æ¡†ï¼Œä»£è¡¨æ­£åœ¨æ‹–动的item
 */
void CEqsGraphWnd::DrawDropItemRectangle(LPRECT lpRect1, LPRECT lpRect2)
{
    HDC hDC = GetDC(m_hWnd);
    HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
    HPEN hPen = CreatePen(PS_DASH, 1, RGB(0, 0, 0));
    int oldRop = SetROP2(hDC, R2_NOTXORPEN);
    HBRUSH hOldBrush = (HBRUSH)::SelectObject(hDC, hBrush);
    HBRUSH hOldPen = (HBRUSH)::SelectObject(hDC, hPen);
    if (lpRect1 != NULL) {
        ::Rectangle(hDC, lpRect1->left, lpRect1->top, lpRect1->right, lpRect1->bottom);
    }
    if (lpRect2 != NULL) {
        ::Rectangle(hDC, lpRect2->left, lpRect2->top, lpRect2->right, lpRect2->bottom);
    }
    ::SetROP2(hDC, oldRop);
    ::SelectObject(hDC, hOldBrush);
    ::SelectObject(hDC, hPen);
    ::DeleteObject(hBrush);
    ::DeleteObject(hOldPen);
    ::ReleaseDC(m_hWnd, hDC);
}
/*
 * ç»˜åˆ¶ç£å¸çº¿
 */
void CEqsGraphWnd::DrawMagneticLine(LPRECT lprcClient, int nHozLine1, int nHozLine2, int nVerLine1, int nVerLine2)
{
    HDC hDC = GetDC(m_hWnd);
    HPEN hPen = CreatePen(PS_DASH, 1, RGB(64, 64, 64));
    int oldRop = SetROP2(hDC, R2_NOTXORPEN);
    HBRUSH hOldPen = (HBRUSH)::SelectObject(hDC, hPen);
    if (nHozLine1) {
        ::MoveToEx(hDC, 1, nHozLine1, NULL);
        ::LineTo(hDC, lprcClient->right-1, nHozLine1);
    }
    if (nHozLine2) {
        ::MoveToEx(hDC, 1, nHozLine2, NULL);
        ::LineTo(hDC, lprcClient->right - 1, nHozLine2);
    }
    if (nVerLine1) {
        ::MoveToEx(hDC, nVerLine1, 1, NULL);
        ::LineTo(hDC, nVerLine1, lprcClient->bottom - 1);
    }
    if (nVerLine2) {
        ::MoveToEx(hDC, nVerLine2, 1, NULL);
        ::LineTo(hDC, nVerLine2, lprcClient->bottom - 1);
    }
    ::SetROP2(hDC, oldRop);
    ::SelectObject(hDC, hPen);
    ::DeleteObject(hOldPen);
    ::ReleaseDC(m_hWnd, hDC);
}
/*
 * ç¼“制Pin连接线
 * pBrush -- ç”»åˆ·
 * pPen - ç”»ç¬”
 * lpPt1, lpPt2 -- Pin脚的位置
 * lpRect1, lpRect2 -- ä¸¤ä¸ªItem的Rect
 */
void CEqsGraphWnd::DrawPinConnectedLine(Gdiplus::Graphics *pGraphics, Gdiplus::Brush *pBrush, Gdiplus::Pen *pPen, LPPOINT lpPt1, LPPOINT lpPt2,
    LPRECT lpRect1, LPRECT lpRect2, PIN *pOwnerPin)
{
    // å¦‚果没有缓存线条的POINT,则先计算并缓存
    ASSERT(pOwnerPin);
    int nPinCount = ((CPtrArray*)pOwnerPin->pItem->pOutPins)->GetSize();
    int nArrowLen = 8;
    int nStartMinX = 8;
    int nMargin = 12;
    int x1, x2, y1;
    if (pOwnerPin->nLinePtCount == 0) {                        // ç¬¬ä¸€ä¸ªç‚¹çš„æœ€å°æŠ˜çº¿é•¿
        ::OffsetRect(lpRect1, +m_nOffsetX, +m_nOffsetY);
        ::OffsetRect(lpRect2, +m_nOffsetX, +m_nOffsetY);
        lpPt1->x += m_nOffsetX;                // æ¶ˆé™¤åç§»
        lpPt1->y += m_nOffsetY;
        lpPt2->x += m_nOffsetX;
        lpPt2->y += m_nOffsetY;
        int nMinX = 10 + nMargin * nPinCount + nArrowLen;
        int xEnd = lpPt2->x - 5;
        x1 = lpPt1->x + 10 + pOwnerPin->nIndex * nMargin;
        if (lpPt2->x - lpPt1->x > nMinX) {
            pOwnerPin->ptConnectedLine[0].x = lpPt1->x;
            pOwnerPin->ptConnectedLine[0].y = lpPt1->y;
            pOwnerPin->ptConnectedLine[1].x = x1;
            pOwnerPin->ptConnectedLine[1].y = lpPt1->y;
            pOwnerPin->ptConnectedLine[2].x = x1;
            pOwnerPin->ptConnectedLine[2].y = lpPt2->y;
            pOwnerPin->ptConnectedLine[3].x = xEnd;
            pOwnerPin->ptConnectedLine[3].y = lpPt2->y;
            pOwnerPin->nLinePtCount = 4;
        }
        else if (lpRect1 != NULL && lpRect2 != NULL) {
            if (lpRect2->top - lpRect1->bottom > 20 || lpRect1->top - lpRect2->bottom > 20) {
                if (lpRect2->top - lpRect1->bottom > 20) {
                    y1 = lpRect1->bottom + 10 + pOwnerPin->nIndex * nMargin;
                    x2 = min(lpPt2->x - nArrowLen, x1) - (nPinCount - pOwnerPin->nIndex) * nMargin;
                }
                else {
                    y1 = lpRect1->top - 10 - pOwnerPin->nIndex * nMargin;
                    x2 = min(lpPt2->x - nArrowLen, x1) - (nPinCount - pOwnerPin->nIndex) * nMargin;
                }
                pOwnerPin->ptConnectedLine[0].x = lpPt1->x;
                pOwnerPin->ptConnectedLine[0].y = lpPt1->y;
                pOwnerPin->ptConnectedLine[1].x = x1;
                pOwnerPin->ptConnectedLine[1].y = lpPt1->y;
                pOwnerPin->ptConnectedLine[2].x = x1;
                pOwnerPin->ptConnectedLine[2].y = y1;
                pOwnerPin->ptConnectedLine[3].x = x2;
                pOwnerPin->ptConnectedLine[3].y = y1;
                pOwnerPin->ptConnectedLine[4].x = x2;
                pOwnerPin->ptConnectedLine[4].y = lpPt2->y;
                pOwnerPin->ptConnectedLine[5].x = xEnd;
                pOwnerPin->ptConnectedLine[5].y = lpPt2->y;
                pOwnerPin->nLinePtCount = 6;
            }
            else {
                x2 = min(lpRect1->left, lpRect2->left) - 30;
                y1 = max(lpRect1->bottom, lpRect2->bottom) + 30;
                pOwnerPin->ptConnectedLine[0].x = lpPt1->x;
                pOwnerPin->ptConnectedLine[0].y = lpPt1->y;
                pOwnerPin->ptConnectedLine[1].x = x1;
                pOwnerPin->ptConnectedLine[1].y = lpPt1->y;
                pOwnerPin->ptConnectedLine[2].x = x1;
                pOwnerPin->ptConnectedLine[2].y = y1;
                pOwnerPin->ptConnectedLine[3].x = x2;
                pOwnerPin->ptConnectedLine[3].y = y1;
                pOwnerPin->ptConnectedLine[4].x = x2;
                pOwnerPin->ptConnectedLine[4].y = lpPt2->y;
                pOwnerPin->ptConnectedLine[5].x = xEnd;
                pOwnerPin->ptConnectedLine[5].y = lpPt2->y;
                pOwnerPin->nLinePtCount = 6;
            }
        }
    }
    if (pOwnerPin->nLinePtCount >= 2) {
        for (int i = 0; i < pOwnerPin->nLinePtCount - 1; i++) {
            pGraphics->DrawLine(pPen, pOwnerPin->ptConnectedLine[i].x - m_nOffsetX, pOwnerPin->ptConnectedLine[i].y - m_nOffsetY,
                pOwnerPin->ptConnectedLine[i + 1].x - m_nOffsetX, pOwnerPin->ptConnectedLine[i + 1].y - m_nOffsetY);
        }
        DrawArrow(pGraphics, pBrush, pPen, pOwnerPin->ptConnectedLine[pOwnerPin->nLinePtCount-1].x - m_nOffsetX,
            pOwnerPin->ptConnectedLine[pOwnerPin->nLinePtCount-1].y - m_nOffsetY, nArrowLen);
    }
}
void CEqsGraphWnd::DrawArrow(Gdiplus::Graphics *pGraphics, Gdiplus::Brush* pBrush, Gdiplus::Pen *pPen,
    int x, int y, int nArrowLen)
{
    Gdiplus::Point pt[4];
    pt[0].X = x;
    pt[0].Y = y;
    pt[1].X = x - nArrowLen;
    pt[1].Y = y - 3;
    pt[2].X = pt[1].X;
    pt[2].Y = y + 3;
    pt[3].X = x;
    pt[3].Y = y;
    pGraphics->FillPolygon(pBrush, pt, 4);
    pGraphics->DrawPolygon(pPen, pt, 4);
}
void CEqsGraphWnd::DrawPinWillConnectLine(COLORREF color, LPPOINT lpPt1, LPPOINT lpPt2)
{
    HDC hDC = GetDC(m_hWnd);
    HPEN hPen = CreatePen(PS_SOLID, 2, color);
    int oldRop = SetROP2(hDC, R2_NOTXORPEN);
    HBRUSH hOldPen = (HBRUSH)::SelectObject(hDC, hPen);
    if (lpPt1 != NULL && lpPt2 != NULL) {
        ::MoveToEx(hDC, lpPt1->x, lpPt1->y, NULL);
        ::LineTo(hDC, lpPt2->x, lpPt2->y);
    }
    ::SetROP2(hDC, oldRop);
    ::SelectObject(hDC, hPen);
    ::DeleteObject(hOldPen);
    ::ReleaseDC(m_hWnd, hDC);
}
/*
 * WindowProc,窗口过程
 */
LRESULT CALLBACK CEqsGraphWnd::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CEqsGraphWnd* pEqsGraphWnd = (CEqsGraphWnd*)GetProp(hWnd, EQSGRAPHWND_TAG);
    if (pEqsGraphWnd == NULL && uMsg != WM_NCCREATE)
    {
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    // å¤„理窗口消息
    ASSERT(hWnd);
    switch (uMsg)
    {
    case WM_NCCREATE:
        return CEqsGraphWnd::OnNcCreate(hWnd, wParam, lParam);
    case WM_DESTROY:
        return pEqsGraphWnd->OnDestroy(wParam, lParam);
    case WM_NCPAINT:
        return pEqsGraphWnd->OnNcPaint(wParam, lParam);
    case WM_PAINT:
        return pEqsGraphWnd->OnPaint(wParam, lParam);
    case WM_TIMER:
        return pEqsGraphWnd->OnTimer(wParam, lParam);
    case WM_MOUSEMOVE:
        return pEqsGraphWnd->OnMouseMove(wParam, lParam);
    case WM_LBUTTONDOWN:
        return pEqsGraphWnd->OnLButtonDown(wParam, lParam);
    case WM_RBUTTONDOWN:
        return pEqsGraphWnd->OnRButtonDown(wParam, lParam);
    case WM_LBUTTONDBLCLK:
        return pEqsGraphWnd->OnLButtonDblclk(wParam, lParam);
    case WM_MOUSEWHEEL:
        return pEqsGraphWnd->OnMouseWheel(wParam, lParam);
    case WM_MOUSEHWHEEL:
        return pEqsGraphWnd->OnMouseHWheel(wParam, lParam);
    case WM_KEYDOWN:
        return pEqsGraphWnd->OnKeyDown(wParam, lParam);
    case WM_SIZE:
        return pEqsGraphWnd->OnSize(wParam, lParam);
    case WM_VSCROLL:
        return pEqsGraphWnd->OnVScroll(wParam, lParam);
    case WM_HSCROLL:
        return pEqsGraphWnd->OnHScroll(wParam, lParam);
    case WM_NOTIFY:
        return pEqsGraphWnd->OnNitify(wParam, lParam);
    case WM_GETDLGCODE:
        return DLGC_WANTALLKEYS;
    default:
        break;
    }
    return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
/*
 * WM_NCCREATE
 * çª—口创建
 */
LRESULT CEqsGraphWnd::OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    CEqsGraphWnd* pEqsGraphWnd = (CEqsGraphWnd*)GetProp(hWnd,EQSGRAPHWND_TAG);
    ASSERT(pEqsGraphWnd == NULL);
    Hook(hWnd)->Init();
    return ::DefWindowProc(hWnd, WM_NCCREATE, wParam, lParam);
}
/*
 * WM_DESTROY
 * çª—口销毁
 */
LRESULT CEqsGraphWnd::OnDestroy(WPARAM wParam, LPARAM lParam)
{
    Release();
    return ::DefWindowProc(m_hWnd, WM_DESTROY, wParam, lParam);
}
/*
 * WM_TIMER
 */
LRESULT CEqsGraphWnd::OnTimer(WPARAM wParam, LPARAM lParam)
{
    if (wParam == TIMER_FLASH) {
        if (m_pFlashItem != NULL && m_nFlashCount > 0) {
            m_nFlashCount--;
            m_pFlashItem->nFlashFlag = (m_nFlashCount % 2);
            RECT rcItem;
            GetItemWarperRect(m_pFlashItem, &rcItem);
            InvalidateRect(m_hWnd, &rcItem, TRUE);
        }
        else {
            m_pFlashItem = NULL;
            m_nFlashCount = 0;
        }
    }
    else if (TIMER_ANIMATION_RECT == wParam) {
        if (m_pAnimationItem != NULL) {
            if (m_nAninationStep > 0) {
                m_nAninationStep--;
                m_rcAnimation.left += m_rcAninationStep.left;
                m_rcAnimation.right += m_rcAninationStep.right;
                m_rcAnimation.top += m_rcAninationStep.top;
                m_rcAnimation.bottom += m_rcAninationStep.bottom;
                RECT rcItem;
                GetItemWarperRect(m_pAnimationItem, &rcItem);
                InvalidateRect(m_hWnd, &rcItem, TRUE);
            }
            else {
                KillTimer(m_hWnd, TIMER_ANIMATION_RECT);
                RECT rcItem;
                GetItemWarperRect(m_pAnimationItem, &rcItem);
                InvalidateRect(m_hWnd, &rcItem, TRUE);
                m_pAnimationItem = NULL;
            }
        }
        else {
            KillTimer(m_hWnd, TIMER_ANIMATION_RECT);
            RECT rcClient;
            GetClientRect(m_hWnd, &rcClient);
            InvalidateRect(m_hWnd, &rcClient, TRUE);
        }
    }
    return ::DefWindowProc(m_hWnd, WM_TIMER, wParam, lParam);
}
/*
 * WM_MOUSEMOVE
 * é¼ æ ‡æ»šåЍ
 */
LRESULT CEqsGraphWnd::OnMouseMove(WPARAM wParam, LPARAM lParam)
{
    return ::DefWindowProc(m_hWnd, WM_MOUSEMOVE, wParam, lParam);
}
/*
 * WM_LBUTTONDOWN
 * é¼ æ ‡å·¦é”®æŒ‰ä¸‹
 */
LRESULT CEqsGraphWnd::OnLButtonDown(WPARAM wParam, LPARAM lParam)
{
    POINT pt, ptNew;
    pt.x = LOWORD(lParam);
    pt.y = HIWORD(lParam);
    RECT rcClient, rcItem, rcNewItem, rcLast;
    GetClientRect(m_hWnd, &rcClient);
    rcLast = {0, 0, 0, 0};
    int nMaxOffsetX = m_nStageCx - (rcClient.right - rcClient.left);
    int nMaxOffsetY = m_nStageCy - (rcClient.bottom - rcClient.top);
    int nLastHozLine = 0;
    int nLastVerLine = 0;
    // æ£€æµ‹ç‚¹å‡»åæ ‡æ˜¯å¦åœ¨æŸä¸€å­é¡¹ä¸Šï¼Œå¦‚是,则高亮显示
    EQITEM* pLastItem = m_pCurItem;
    PIN *pLastPin = m_pCurPin;
    PIN *pLastSelLineOutPin = m_pSelLineOutPin;
    BOOL bChanged = FALSE;
    EQITEM* pHitItem = NULL;
    PIN *pHitPin = NULL;
    PIN *pPin2 = NULL;
    int nRet = HighTest(pt, pHitItem, pHitPin);
    if (pHitItem != m_pCurItem || nRet != HT_ITEM) {
        if (m_pCurItem != NULL) {
            m_pCurItem->bHighlight = FALSE;
        }
        m_pCurItem = NULL;
    }
    if (pHitPin != m_pCurPin || nRet != HT_PIN) {
        if (m_pCurPin != NULL) {
            m_pCurPin->bHighlight = FALSE;
        }
        m_pCurPin = NULL;
    }
    if (pHitPin != m_pSelLineOutPin || nRet != HT_LINE) {
        m_pSelLineOutPin = NULL;
    }
    if (nRet == HT_ITEM) {
        m_pCurItem = pHitItem;
        m_pCurItem->bHighlight = TRUE;
    }
    else if (nRet == HT_PIN) {
        m_pCurPin = pHitPin;
        m_pCurPin->bHighlight = TRUE;
    }
    else if (nRet == HT_LINE) {
        m_pSelLineOutPin = pHitPin;
    }
    bChanged = pLastItem != m_pCurItem || pLastPin != m_pCurPin || pLastSelLineOutPin != m_pSelLineOutPin;
    // åˆ·æ–°
    SetFocus(m_hWnd);
    if (bChanged) {
        ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    }
    // æ•捉鼠标消息,检测是否拖动
    if (nRet == HT_ITEM && m_pCurItem != NULL) {
        GetItemRect(m_pCurItem, &rcItem);
        if (::GetCapture() == NULL) {
            SetCapture(m_hWnd);
            ASSERT(m_hWnd == GetCapture());
            AfxLockTempMaps();
            for (;;)
            {
                MSG msg;
                VERIFY(::GetMessage(&msg, NULL, 0, 0));
                if (GetCapture() != m_hWnd) break;
                switch (msg.message)
                {
                case WM_MOUSEMOVE:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    rcNewItem.left = rcItem.left + (ptNew.x - pt.x);
                    rcNewItem.right = rcItem.right + (ptNew.x - pt.x);
                    rcNewItem.top = rcItem.top + (ptNew.y - pt.y);
                    rcNewItem.bottom = rcItem.bottom + (ptNew.y - pt.y);
                    CalculateMagneticLine(m_pCurItem, &rcNewItem, m_nMagneticLinHoz, m_nMagneticLinVer);
                    DrawDropItemRectangle(&rcNewItem, &rcLast);
                    DrawMagneticLine(&rcClient, m_nMagneticLinHoz, nLastHozLine, m_nMagneticLinVer, nLastVerLine);
                    nLastHozLine = m_nMagneticLinHoz;
                    nLastVerLine = m_nMagneticLinVer;
                    CopyRect(&rcLast, &rcNewItem);
                    break;
                case WM_LBUTTONUP:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    m_pCurItem->rect.left = m_nMagneticLinVer > 0 ? m_nMagneticLinVer : (rcItem.left + (ptNew.x - pt.x) + m_nOffsetX);
                    m_pCurItem->rect.right = m_pCurItem->rect.left + (rcItem.right - rcItem.left);
                    m_pCurItem->rect.top = m_nMagneticLinHoz > 0 ? m_nMagneticLinHoz : (rcItem.top + (ptNew.y - pt.y) + m_nOffsetY);
                    m_pCurItem->rect.bottom = m_pCurItem->rect.top + (rcItem.bottom - rcItem.top);
                    if (m_pCurItem->rect.left != rcItem.left || m_pCurItem->rect.top != rcItem.top) {
                        if (m_listener.onEqItemPosChanged != nullptr) {
                            m_listener.onEqItemPosChanged(m_pCurItem, m_pCurItem->rect.left, m_pCurItem->rect.top);
                        }
                    }
                    DrawDropItemRectangle(NULL, &rcLast);
                    ReleaseCapture();
                    ClearConnectedLinePoint(m_pCurItem);
                    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
                    goto ExitLoop;
                case WM_KEYDOWN:
                    if (msg.wParam != VK_ESCAPE)
                        break;
                default:
                    DispatchMessage(&msg);
                    break;
                }
            }
            ReleaseCapture();
        ExitLoop:
            AfxUnlockTempMaps(FALSE);
        }
    }
    // æ•捉鼠标消息,检测是否连接引脚
    else if (nRet == HT_PIN && m_pCurPin != NULL) {
        if (::GetCapture() == NULL) {
            BOOL bLast = FALSE;
            bool bCanConnect;
            POINT ptPin, ptLast;
            COLORREF lineColor;
            GetPinPoint(m_pCurPin, &ptPin);
            ptLast.x = ptPin.x;
            ptLast.y = ptPin.y;
            SetCapture(m_hWnd);
            ASSERT(m_hWnd == GetCapture());
            AfxLockTempMaps();
            for (;;)
            {
                MSG msg;
                VERIFY(::GetMessage(&msg, NULL, 0, 0));
                if (GetCapture() != m_hWnd) break;
                switch (msg.message)
                {
                case WM_MOUSEMOVE:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    // æ“¦é™¤ä¸Šä¸€æ¬¡
                    if (bLast) {
                        DrawPinWillConnectLine(lineColor, &ptPin, &ptLast);
                    }
                    // æ£€æµ‹æ˜¯å¦å¯ä»¥è¿žæŽ¥
                    bCanConnect = false;
                    nRet = HighTest(ptNew, pHitItem, pHitPin);
                    if (nRet == HT_PIN) {
                        if (m_listener.onCheckConnectPin != nullptr) {
                            bCanConnect = m_listener.onCheckConnectPin(m_pCurPin, pHitPin);
                        }
                    }
                    if (bCanConnect) {
                        lineColor = RGB(0, 255, 0);
                        DrawPinWillConnectLine(lineColor, &ptPin, &ptNew);
                    }
                    else {
                        lineColor = RGB(0, 0, 0);
                        DrawPinWillConnectLine(lineColor, &ptPin, &ptNew);
                    }
                    ptLast.x = ptNew.x;
                    ptLast.y = ptNew.y;
                    bLast = TRUE;
                    break;
                case WM_LBUTTONUP:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    // æ“¦é™¤ä¸Šä¸€æ¬¡
                    if (bLast) {
                        DrawPinWillConnectLine(lineColor, &ptPin, &ptLast);
                    }
                    // æ£€æµ‹æ˜¯å¦å¯ä»¥è¿žæŽ¥
                    bCanConnect = false;
                    nRet = HighTest(ptNew, pHitItem, pHitPin);
                    if (nRet == HT_PIN) {
                        if (m_listener.onConnectPin != nullptr) {
                            bCanConnect = m_listener.onConnectPin(m_pCurPin, pHitPin);
                        }
                    }
                    if (bCanConnect) {
                        m_pCurPin->pConnectedPin = pHitPin;
                        pHitPin->pConnectedPin = m_pCurPin;
                    }
                    bLast = FALSE;
                    ReleaseCapture();
                    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
                    goto ExitLoop2;
                case WM_KEYDOWN:
                    if (msg.wParam != VK_ESCAPE)
                        break;
                default:
                    DispatchMessage(&msg);
                    break;
                }
            }
            ReleaseCapture();
        ExitLoop2:
            AfxUnlockTempMaps(FALSE);
        }
    }
    // æ£€æµ‹é¼ æ ‡æ¶ˆæ¯ï¼Œæ£€æµ‹æ˜¯å¦ç§»åŠ¨ç”»å¸ƒ
    else if (nRet == HT_NOWHERE) {
        if (::GetCapture() == NULL) {
            int nLastOffsetX = m_nOffsetX;
            int nLastOffsetY = m_nOffsetY;
            POINT ptStart;
            ptStart.x = pt.x;
            ptStart.y = pt.y;
            SetCursor(LoadCursor(NULL, IDC_SIZEALL));
            SetCapture(m_hWnd);
            ASSERT(m_hWnd == GetCapture());
            AfxLockTempMaps();
            for (;;) {
                MSG msg;
                VERIFY(::GetMessage(&msg, NULL, 0, 0));
                if (GetCapture() != m_hWnd) break;
                switch (msg.message)
                {
                case WM_MOUSEMOVE:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    m_nOffsetX = min(nMaxOffsetX, max(0, nLastOffsetX - (ptNew.x - ptStart.x)));
                    m_nOffsetY = min(nMaxOffsetY, max(0, nLastOffsetY - (ptNew.y - ptStart.y)));
                    CalculateScollbar();
                    CalculateMapPos();
                    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
                    break;
                case WM_LBUTTONUP:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    ReleaseCapture();
                    CalculateScollbar();
                    CalculateMapPos();
                    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
                    goto ExitLoop3;
                case WM_KEYDOWN:
                    if (msg.wParam != VK_ESCAPE)
                        break;
                default:
                    DispatchMessage(&msg);
                    break;
                }
            }
            ReleaseCapture();
        ExitLoop3:
            AfxUnlockTempMaps(FALSE);
        }
    }
    return ::DefWindowProc(m_hWnd, WM_LBUTTONDOWN, wParam, lParam);
}
/*
 * WM_LBUTTONDBLCLK
 * é¼ æ ‡å·¦é”®åŒå‡»
 */
LRESULT CEqsGraphWnd::OnLButtonDblclk(WPARAM wParam, LPARAM lParam)
{
    POINT pt;
    pt.x = LOWORD(lParam);
    pt.y = HIWORD(lParam);
    RECT rcClient, rcLast;
    GetClientRect(m_hWnd, &rcClient);
    rcLast = { 0, 0, 0, 0 };
    // æ£€æµ‹ç‚¹å‡»åæ ‡æ˜¯å¦åœ¨æŸä¸€å­é¡¹ä¸Šï¼Œå¦‚是,则高亮显示
    EQITEM* pLastItem = m_pCurItem;
    BOOL bChanged = FALSE;
    EQITEM* pHitItem = NULL;
    PIN *pHitPin = NULL;
    int nRet = HighTest(pt, pHitItem, pHitPin);
    if ( nRet == HT_ITEM) {
        m_pCurItem = pHitItem;
        m_pCurItem->bHighlight = FALSE;
        if (m_listener.onDblckEqItem != nullptr) {
            m_listener.onDblckEqItem(pHitItem);
        }
    }
    return ::DefWindowProc(m_hWnd, WM_LBUTTONDBLCLK, wParam, lParam);
}
/*
 * WM_MOUSEWHEEL
 * é¼ æ ‡æ»šåЍ
 */
LRESULT CEqsGraphWnd::OnMouseWheel(WPARAM wParam, LPARAM lParam)
{
    short zDelta;
    UINT nFlags;
    CPoint pt;
    nFlags = LOWORD(wParam);
    zDelta = (short)HIWORD(wParam);
    pt.x = (short)LOWORD(lParam);
    pt.y = (short)HIWORD(lParam);
    CRect rcClient;
    GetClientRect(m_hWnd, &rcClient);
    m_nOffsetY -= zDelta;
    m_nOffsetY = max(0, min(m_nOffsetY, m_nStageCy - rcClient.Height()));
    CalculateScollbar();
    CalculateMapPos();
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    return ::DefWindowProc(m_hWnd, WM_MOUSEWHEEL, wParam, lParam);
}
/*
* WM_MOUSEHWHEEL
* é¼ æ ‡æ»šåЍ
*/
LRESULT CEqsGraphWnd::OnMouseHWheel(WPARAM wParam, LPARAM lParam)
{
    short zDelta;
    UINT nFlags;
    CPoint pt;
    nFlags = LOWORD(wParam);
    zDelta = (short)HIWORD(wParam);
    pt.x = (short)LOWORD(lParam);
    pt.y = (short)HIWORD(lParam);
    CRect rcClient;
    GetClientRect(m_hWnd, &rcClient);
    m_nOffsetX += zDelta;
    m_nOffsetX = max(0, min(m_nOffsetX, m_nStageCx - rcClient.Width()));
    CalculateScollbar();
    CalculateMapPos();
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    return ::DefWindowProc(m_hWnd, WM_MOUSEHWHEEL, wParam, lParam);
}
/*
 * WM_RBUTTONDOWN
 * é¼ æ ‡å·¦é”®æŒ‰ä¸‹
 */
LRESULT CEqsGraphWnd::OnRButtonDown(WPARAM wParam, LPARAM lParam)
{
    POINT pt, ptNew;
    pt.x = LOWORD(lParam);
    pt.y = HIWORD(lParam);
    RECT rcClient, rcItem, rcLast;
    GetClientRect(m_hWnd, &rcClient);
    rcLast = { 0, 0, 0, 0 };
    // æ£€æµ‹ç‚¹å‡»åæ ‡æ˜¯å¦åœ¨æŸä¸€å­é¡¹ä¸Šï¼Œå¦‚是,则高亮显示
    EQITEM* pLastItem = m_pCurItem;
    PIN *pLastPin = m_pCurPin;
    PIN *pLastSelLineOutPin = m_pSelLineOutPin;
    BOOL bChanged = FALSE;
    EQITEM* pHitItem = NULL;
    PIN *pHitPin = NULL;
    PIN *pPin2 = NULL;
    int nRet = HighTest(pt, pHitItem, pHitPin);
    if (pHitItem != m_pCurItem || nRet != HT_ITEM) {
        if (m_pCurItem != NULL) {
            m_pCurItem->bHighlight = FALSE;
        }
        m_pCurItem = NULL;
    }
    if (pHitPin != m_pCurPin || nRet != HT_PIN) {
        if (m_pCurPin != NULL) {
            m_pCurPin->bHighlight = FALSE;
        }
        m_pCurPin = NULL;
    }
    if (pHitPin != m_pSelLineOutPin || nRet != HT_LINE) {
        m_pSelLineOutPin = NULL;
    }
    if (nRet == HT_ITEM) {
        m_pCurItem = pHitItem;
        m_pCurItem->bHighlight = TRUE;
    }
    else if (nRet == HT_PIN) {
        m_pCurPin = pHitPin;
        m_pCurPin->bHighlight = TRUE;
    }
    else if (nRet == HT_LINE) {
        m_pSelLineOutPin = pHitPin;
    }
    bChanged = pLastItem != m_pCurItem || pLastPin != m_pCurPin || pLastSelLineOutPin != m_pSelLineOutPin;
    // åˆ·æ–°
    SetFocus(m_hWnd);
    if (bChanged) {
        ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    }
    // æ•捉鼠标消息,检测是否拖动
    if (nRet == HT_ITEM && m_pCurItem != NULL) {
        CopyRect(&rcItem, &m_pCurItem->rect);
        if (::GetCapture() == NULL) {
            SetCapture(m_hWnd);
            ASSERT(m_hWnd == GetCapture());
            AfxLockTempMaps();
            for (;;)
            {
                MSG msg;
                VERIFY(::GetMessage(&msg, NULL, 0, 0));
                if (GetCapture() != m_hWnd) break;
                switch (msg.message)
                {
                case WM_MOUSEMOVE:
                    break;
                case WM_RBUTTONUP:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    nRet = HighTest(ptNew, pHitItem, pHitPin);
                    ReleaseCapture();
                    if (m_listener.onRclickEqItem != NULL) {
                        m_listener.onRclickEqItem(pHitItem);
                    }
                    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
                    goto ExitLoop;
                case WM_KEYDOWN:
                    if (msg.wParam != VK_ESCAPE)
                        break;
                default:
                    DispatchMessage(&msg);
                    break;
                }
            }
            ReleaseCapture();
        ExitLoop:
            AfxUnlockTempMaps(FALSE);
        }
    }
    return ::DefWindowProc(m_hWnd, WM_LBUTTONDOWN, wParam, lParam);
}
/*
 * WM_KEYDOWN
 * é”®ç›˜æ¶ˆæ¯ï¼ŒæŒ‰ä¸‹æŒ‰é”®
 */
LRESULT CEqsGraphWnd::OnKeyDown(WPARAM wParam, LPARAM lParam)
{
    BOOL bChanged = FALSE;
    if (wParam == VK_DELETE) {
        // å¦‚果当前选择为线,则断开连接
        if (m_pSelLineOutPin != NULL) {
            if (m_listener.onDisconnectPin != nullptr) {
                if (m_listener.onDisconnectPin(m_pSelLineOutPin)) {
                    m_pSelLineOutPin->pConnectedPin->pConnectedPin = NULL;
                    m_pSelLineOutPin->pConnectedPin = NULL;
                    m_pSelLineOutPin = NULL;
                    bChanged = TRUE;
                }
            }
        }
        else if (m_pCurItem != NULL) {
            if (m_listener.onDeleteEqItem != NULL) {
                if (m_listener.onDeleteEqItem(m_pCurItem)) {
                    bChanged = DeleteItem(m_pCurItem) >= 0;
                }
            }
        }
    }
    if (bChanged) {
        RECT rcClient;
        GetClientRect(m_hWnd, &rcClient);
        ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    }
    return ::DefWindowProc(m_hWnd, WM_KEYDOWN, wParam, lParam);
}
/*
 * WM_NCPAINT
 */
LRESULT CEqsGraphWnd::OnNcPaint(WPARAM wParam, LPARAM lParam)
{
    LRESULT lRet = ::DefWindowProc(m_hWnd, WM_NCPAINT, wParam, lParam);
    long styleEx = GetWindowLong(m_hWnd, GWL_EXSTYLE);
    if ((styleEx & WS_EX_CLIENTEDGE) == WS_EX_CLIENTEDGE) {
        RECT rect, rcClient;
        GetClientRect(m_hWnd, &rcClient);
        ::ClientToScreen(m_hWnd, (LPPOINT)&rcClient.left);
        ::ClientToScreen(m_hWnd, (LPPOINT)&rcClient.right);
        GetWindowRect(m_hWnd, &rect);
        rcClient.right = rect.right - 1;
        rcClient.bottom = rect.bottom - 1;
        ::OffsetRect(&rcClient, -rect.left, -rect.top);
        rect.right -= rect.left;
        rect.bottom -= rect.top;
        rect.left = 0;
        rect.top = 0;
        HRGN hRgnWnd = CreateRectRgnIndirect(&rect);
        HRGN hRgnClient = CreateRectRgnIndirect(&rcClient);
        HBRUSH hBrushBK, hBrushFrame;
        HDC hDC = ::GetWindowDC(m_hWnd);
        ::SelectClipRgn(hDC, hRgnWnd);
        ::ExtSelectClipRgn(hDC, hRgnClient, RGN_DIFF);
        hBrushBK = CreateSolidBrush(m_crBkgnd);
        ::FillRect(hDC, &rect, hBrushBK);
        DeleteObject(hBrushBK);
        hBrushFrame = CreateSolidBrush(m_crFrame);
        ::FrameRect(hDC, &rect, hBrushFrame);
        ::DeleteObject(hRgnWnd);
        ::DeleteObject(hRgnClient);
        DeleteObject(hBrushFrame);
        ::ReleaseDC(m_hWnd, hDC);
    }
    return lRet;
}
/*
 * WM_PAINT
 */
LRESULT CEqsGraphWnd::OnPaint(WPARAM wParam, LPARAM lParam)
{
    HDC hDC, hMemDC;
    HBITMAP hBitmap;
    RECT rcClient;
    CString strText;
    HBRUSH hBrushBK;
    // BeginPaint
    PAINTSTRUCT ps;
    hDC = BeginPaint(m_hWnd, &ps);
    GetClientRect(m_hWnd, &rcClient);
    hMemDC = ::CreateCompatibleDC(hDC);
    hBitmap = ::CreateCompatibleBitmap(hDC, rcClient.right - rcClient.left,
        rcClient.bottom - rcClient.top);
    ::SelectObject(hMemDC, hBitmap);
    // èƒŒæ™¯é¢œè‰²
    hBrushBK = CreateSolidBrush(m_crBkgnd);
    ::FillRect(hMemDC, &rcClient, hBrushBK);
    DeleteObject(hBrushBK);
    // æ ‡é¢˜
    if (m_hFontTitle == nullptr) {
        LOGFONT lf;
        HFONT hFontDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
        ::GetObject(hFontDefault, sizeof(LOGFONT), &lf);
        lf.lfHeight -= 6;
        lf.lfWeight = FW_SEMIBOLD;
        m_hFontTitle = CreateFontIndirect(&lf);
    }
    {
        char szTitle[256];
        GetWindowText(m_hWnd, szTitle, 256);
        RECT rcTitle;
        rcTitle.left = rcClient.left + 5;
        rcTitle.top = rcClient.top + 12;
        rcTitle.bottom = rcClient.bottom - 5;
        rcTitle.right = rcClient.right - 5;
        ::SelectObject(hMemDC, m_hFontTitle);
        ::DrawText(hMemDC, szTitle, (int)strlen(szTitle), &rcTitle, DT_LEFT | DT_TOP);
    }
    // ç»˜åˆ¶å­é¡¹
    HBRUSH hbrItemBackground[2];
    HBRUSH hbrItemFrame[2];
    HBRUSH hbrPinBackground[3];
    hbrItemBackground[0] = CreateSolidBrush(m_crItemBackground[0]);
    hbrItemBackground[1] = CreateSolidBrush(m_crItemBackground[1]);
    hbrItemFrame[0] = CreateSolidBrush(m_crItemFrame[0]);
    hbrItemFrame[1] = CreateSolidBrush(m_crItemFrame[1]);
    for (int i = 0; i < 3; i++) {
        hbrPinBackground[i] = CreateSolidBrush(m_crPinBkgnd[i]);
    }
    // gdi+
    Gdiplus::Graphics graphics(hMemDC);
    Gdiplus::Pen pen1(Gdiplus::Color(255, 64, 64, 64), 2);
    Gdiplus::Pen pen2(Gdiplus::Color(255, 255, 127, 39), 2);
    Gdiplus::SolidBrush brush1(Gdiplus::Color(255, 64, 64, 64));
    if (m_bUseGdiPlus) {
        graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
    }
    SetBkMode(hMemDC, TRANSPARENT);
    {
        RECT rcItem;
        int nPinState;
        int nItemCount = (int)m_arItem.GetCount();
        for (int i = 0; i < nItemCount; i++) {
            EQITEM* pItem = (EQITEM*)m_arItem.GetAt(i);
            if (pItem->nFlashFlag == 1) {
                continue;
            }
            GetItemRect(pItem, &rcItem);
            // å­é¡¹èƒŒæ™¯å’Œè¾¹æ¡†
            if (m_nItemRound == 0) {
                ::FillRect(hMemDC, &rcItem, pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0]);
                ::FrameRect(hMemDC, &rcItem, pItem->bHighlight ? hbrItemFrame[1] : hbrItemFrame[0]);
            }
            else {
                HRGN hRgn = CreateRoundRectRgn(rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, m_nItemRound, m_nItemRound);
                ::FillRgn(hMemDC, hRgn, pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0]);
                ::FrameRgn(hMemDC, hRgn, pItem->bHighlight ? hbrItemFrame[1] : hbrItemFrame[0], 1, 1);
                ::DeleteObject(hRgn);
            }
            // name和id
            HFONT hFontOld = (HFONT)::SelectObject(hMemDC, m_hFontName);
            ::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemNameText[1] : m_crItemNameText[0]);
            ::DrawText(hMemDC, pItem->text, (int)strlen(pItem->text), &rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
            if (pItem->nShowType != ITEM_SMALL) {
                RECT rcId = rcItem;
                rcId.left += 5;
                rcId.bottom -= 5;
                CString strId;
                strId.Format(_T("ID:%d"), pItem->id);
                ::SelectObject(hMemDC, m_hFontId);
                ::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemIdText[1] : m_crItemIdText[0]);
                ::DrawText(hMemDC, strId, (int)strId.GetLength(), &rcId, DT_LEFT | DT_BOTTOM | DT_SINGLELINE | DT_END_ELLIPSIS);
            }
            // åŠ¨ç”»æ•ˆæžœä¸ç»˜pin
            if (m_pAnimationItem == pItem) {
                continue;
            }
            // ç»˜åˆ¶pin
            RECT rcPin, rcPin2, rcPinText;
            CPtrArray *pPins;
            rcPinText.left = rcItem.left + 8;
            rcPinText.right = rcItem.right - 8;
            // in pins
            PIN *pPin = NULL;
            pPins = (CPtrArray *)pItem->pInPins;
            for (int j = 0; j < pPins->GetSize(); j++) {
                if (GetInPinRect(pItem, j, &rcPin)) {
                    pPin = (PIN *)pPins->GetAt(j);
                    ::FrameRect(hMemDC, &rcPin, pItem->bHighlight ? hbrItemFrame[1] : hbrItemFrame[0]);
                    rcPin2.left = rcPin.left + 1;
                    rcPin2.right = rcPin.right;
                    rcPin2.top = rcPin.top + 1;
                    rcPin2.bottom = rcPin.bottom - 1;
                    nPinState = GetPinState(pPin);
                    ::FillRect(hMemDC, &rcPin2, nPinState == 0 ? (pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0]) : hbrPinBackground[nPinState]);
                    if (pItem->nShowType != ITEM_SMALL) {
                        rcPinText.top = rcPin.top - 12;
                        rcPinText.bottom = rcPin.bottom + 12;
                        ::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemIdText[1] : m_crItemIdText[0]);
                        ::DrawText(hMemDC, pPin->text, (int)strlen(pPin->text), &rcPinText, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
                    }
                }
            }
            // out pins
            pPins = (CPtrArray *)pItem->pOutPins;
            for (int j = 0; j < pPins->GetSize(); j++) {
                pPin = (PIN *)pPins->GetAt(j);
                if (GetOutPinRect(pItem, j, &rcPin)) {
                    ::FrameRect(hMemDC, &rcPin, pItem->bHighlight ? hbrItemFrame[1] : hbrItemFrame[0]);
                    rcPin2.left = rcPin.left;
                    rcPin2.right = rcPin.right - 1;
                    rcPin2.top = rcPin.top + 1;
                    rcPin2.bottom = rcPin.bottom - 1;
                    nPinState = GetPinState(pPin);
                    ::FillRect(hMemDC, &rcPin2, nPinState == 0 ? (pItem->bHighlight ? hbrItemBackground[1] : hbrItemBackground[0]) : hbrPinBackground[nPinState]);
                    if (pItem->nShowType != ITEM_SMALL) {
                        rcPinText.top = rcPin.top - 12;
                        rcPinText.bottom = rcPin.bottom + 12;
                        ::SetTextColor(hMemDC, pItem->bHighlight ? m_crItemIdText[1] : m_crItemIdText[0]);
                        ::DrawText(hMemDC, pPin->text, (int)strlen(pPin->text), &rcPinText, DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
                    }
                }
            }
            ::DeleteObject(hbrItemFrame);
            ::SelectObject(hMemDC, hFontOld);
        }
        // ç»˜åˆ¶è¿žæŽ¥çº¿ï¼Œä¿å­˜çº¿æ¡åœ¨æœ€åŽç»˜åˆ¶
        for (int i = 0; i < nItemCount; i++) {
            EQITEM *pItem = (EQITEM*)m_arItem.GetAt(i);
            if (pItem->nFlashFlag == 1) {
                continue;
            }
            PIN *pPin = NULL;
            CPtrArray *pPins;
            // out pins边线
            RECT rcItem1, rcItem2;
            pPins = (CPtrArray *)pItem->pOutPins;
            for (int j = 0; j < pPins->GetSize(); j++) {
                pPin = (PIN *)pPins->GetAt(j);
                if (pPin->pConnectedPin != NULL) {
                    POINT pt1, pt2;
                    if (GetPinPoint(pPin, &pt1) && GetPinPoint(pPin->pConnectedPin, &pt2)) {
                        GetItemRect(pItem, &rcItem1);
                        GetItemRect(pPin->pConnectedPin->pItem, &rcItem2);
                        DrawPinConnectedLine(&graphics, &brush1, pPin == m_pSelLineOutPin ? &pen2 : &pen1,
                            &pt1, &pt2, &rcItem1, &rcItem2, pPin);
                    }
                }
            }
        }
        for (int i = 0; i < 3; i++) {
            ::DeleteObject(hbrPinBackground[i]);
        }
        ::DeleteObject(hbrItemBackground[0]);
        ::DeleteObject(hbrItemBackground[1]);
        ::DeleteObject(hbrItemFrame[0]);
        ::DeleteObject(hbrItemFrame[1]);
    }
    // EndPaint
    ::BitBlt(hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
        hMemDC, 0, 0, SRCCOPY);
    EndPaint(m_hWnd, &ps);
    ::DeleteObject(hBitmap);
    ::DeleteDC(hMemDC);
    return 1;
}
/*
 * WM_SIZE
 */
LRESULT CEqsGraphWnd::OnSize(WPARAM wParam, LPARAM lParam)
{
    LRESULT lRet = ::DefWindowProc(m_hWnd, WM_SIZE, wParam, lParam);
    CalculateScollbar();
    if (m_hWndMapPos != NULL) {
        CalculateMapPos();
        CRect rcItem, rcClient;
        GetClientRect(m_hWnd, &rcClient);
        GetWindowRect(m_hWndMapPos,& rcItem);
        ::MoveWindow(m_hWndMapPos, rcClient.right- rcItem.Width() - MAPPOSWND_PADDING_RIGHT,
            MAPPOSWND_PADDING_RIGHT, rcItem.Width(), rcItem.Height(), TRUE);
    }
    return lRet;
}
/*
 * WM_VSCROLL
 */
LRESULT CEqsGraphWnd::OnVScroll(WPARAM wParam, LPARAM lParam)
{
    int nSBCode = LOWORD(wParam);
    int nPos = HIWORD(wParam);
    SCROLLINFO info = { 0 };
    info.cbSize = sizeof(SCROLLINFO);
    info.fMask = SIF_ALL;
    GetScrollInfo(m_hWnd, SB_VERT, &info);
    int nMaxPos = info.nMax - info.nPage;
    int inc = 10;
    switch (nSBCode)
    {
    case SB_BOTTOM:
        if (info.nPos < nMaxPos) {
            // ScrollWindow(m_hWnd, 0, -1 * inc*(iMaxPos - info.nPos), NULL, NULL);
            info.nPos = nMaxPos;
        }
        break;
    case SB_TOP:
        if (info.nPos > info.nMin) {
            // ScrollWindow(m_hWnd, 0, inc*(info.nPos - info.nMin), NULL, NULL);
            info.nPos = info.nMin;
        }
        break;
    case SB_LINEUP:
        if (info.nPos > info.nMin) {
            //ScrollWindow(m_hWnd, 0, inc, NULL, NULL);
            info.nPos -= 1;
        }
        break;
    case SB_LINEDOWN:
        if (info.nPos < nMaxPos) {
            // ScrollWindow(m_hWnd, 0, -1 * inc, NULL, NULL);
            info.nPos += 1;
        }
        break;
    case SB_PAGEUP:
        if (info.nPos - 100 >= info.nMin) {
            //ScrollWindow(m_hWnd, 0, 100 * inc, NULL, NULL);
            info.nPos -= 100;
        }
        else {
            if (info.nPos <= 0) {
                // ScrollWindow(m_hWnd, 0, 0, NULL, NULL);
            }
            else {
                // ScrollWindow(m_hWnd, 0, info.nPos, NULL, NULL);
            }
            info.nPos = info.nMin;
        }
        break;
    case SB_PAGEDOWN:
        if (info.nPos + 100 <= nMaxPos) {
            // ScrollWindow(m_hWnd, 0, -100*inc, NULL, NULL);
            info.nPos += 100;
        }
        else {
            // ScrollWindow(m_hWnd, 0, (info.nPos - iMaxPos) * inc, NULL, NULL);
            info.nPos = nMaxPos;
        }
        break;
    case SB_ENDSCROLL:
        break;
    case SB_THUMBPOSITION:
        break;
    case SB_THUMBTRACK:
        // ScrollWindow(m_hWnd, 0, inc * (info.nPos - nPos), NULL, NULL);
        info.nPos = nPos;
        break;
    default:
        break;
    }
    m_nOffsetY = info.nPos;
    SetScrollInfo(m_hWnd, SB_VERT, &info, TRUE);
    CalculateMapPos();
    RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    LRESULT lRet = ::DefWindowProc(m_hWnd, WM_VSCROLL, wParam, lParam);
    return lRet;
}
/*
 * WM_HSCROLL
 */
LRESULT CEqsGraphWnd::OnHScroll(WPARAM wParam, LPARAM lParam)
{
    int nSBCode = LOWORD(wParam);
    int nPos = HIWORD(wParam);
    SCROLLINFO info = { 0 };
    info.cbSize = sizeof(SCROLLINFO);
    info.fMask = SIF_ALL;
    GetScrollInfo(m_hWnd, SB_HORZ, &info);
    int nMaxPos = info.nMax - info.nPage;
    int inc = 10;
    switch (nSBCode)
    {
    case SB_RIGHT:
        if (info.nPos < nMaxPos) {
            // ScrollWindow(m_hWnd, 0, -1 * inc*(iMaxPos - info.nPos), NULL, NULL);
            info.nPos = nMaxPos;
        }
        break;
    case SB_LEFT:
        if (info.nPos > info.nMin) {
            // ScrollWindow(m_hWnd, 0, inc*(info.nPos - info.nMin), NULL, NULL);
            info.nPos = info.nMin;
        }
        break;
    case SB_LINELEFT:
        if (info.nPos > info.nMin) {
            //ScrollWindow(m_hWnd, 0, inc, NULL, NULL);
            info.nPos -= 1;
        }
        break;
    case SB_LINERIGHT:
        if (info.nPos < nMaxPos) {
            // ScrollWindow(m_hWnd, 0, -1 * inc, NULL, NULL);
            info.nPos += 1;
        }
        break;
    case SB_PAGELEFT:
        if (info.nPos - 100 >= info.nMin) {
            //ScrollWindow(m_hWnd, 0, 100 * inc, NULL, NULL);
            info.nPos -= 100;
        }
        else {
            if (info.nPos <= 0) {
                // ScrollWindow(m_hWnd, 0, 0, NULL, NULL);
            }
            else {
                // ScrollWindow(m_hWnd, 0, info.nPos, NULL, NULL);
            }
            info.nPos = info.nMin;
        }
        break;
    case SB_PAGERIGHT:
        if (info.nPos + 100 <= nMaxPos) {
            // ScrollWindow(m_hWnd, 0, -100*inc, NULL, NULL);
            info.nPos += 100;
        }
        else {
            // ScrollWindow(m_hWnd, 0, (info.nPos - iMaxPos) * inc, NULL, NULL);
            info.nPos = nMaxPos;
        }
        break;
    case SB_ENDSCROLL:
        break;
    case SB_THUMBPOSITION:
        break;
    case SB_THUMBTRACK:
        // ScrollWindow(m_hWnd, 0, inc * (info.nPos - nPos), NULL, NULL);
        info.nPos = nPos;
        break;
    default:
        break;
    }
    m_nOffsetX = info.nPos;
    SetScrollInfo(m_hWnd, SB_HORZ, &info, TRUE);
    CalculateMapPos();
    RECT rcClient;
    GetClientRect(m_hWnd, &rcClient);
    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    LRESULT lRet = ::DefWindowProc(m_hWnd, WM_HSCROLL, wParam, lParam);
    return lRet;
}
/*
 * WM_NOTIFY
 */
LRESULT CEqsGraphWnd::OnNitify(WPARAM wParam, LPARAM lParam)
{
    LRESULT lRet = ::DefWindowProc(m_hWnd, WM_NOTIFY, wParam, lParam);
    NMHDR *pNmhdr = (NMHDR *)lParam;
    if (pNmhdr->idFrom == MAPPOSWND_ID) {
        MAPPOSWND_NMHDR *pNmhdr2 = (MAPPOSWND_NMHDR *)lParam;
        m_nOffsetX = pNmhdr2->dwData;
        m_nOffsetY = pNmhdr2->dwData1;
        CalculateScollbar();
        CalculateMapPos();
        RECT rcClient;
        GetClientRect(m_hWnd, &rcClient);
        ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    }
    return lRet;
}
/*
 * è®¾ç½®èƒŒæ™¯é¢œè‰²
 * color -- èƒŒæ™¯è‰²
 */
void CEqsGraphWnd::SetBkgndColor(COLORREF color)
{
    m_crBkgnd = color;
}
/*
 * è¾¹æ¡†é¢œè‰²
 * color -- è¾¹æ¡†è‰²
 */
void CEqsGraphWnd::SetFrameColor(COLORREF color)
{
    m_crFrame = color;
}
void CEqsGraphWnd::FlashItem(EQITEM *pItem)
{
    if (m_pFlashItem != NULL) {
        KillTimer(m_hWnd, TIMER_FLASH);
    }
    m_nFlashCount = 5;
    m_pFlashItem = pItem;
    SetTimer(m_hWnd, TIMER_FLASH, 100, NULL);
}
void CEqsGraphWnd::AnimationItem(EQITEM* pItem)
{
    if (m_pAnimationItem != NULL) {
        KillTimer(m_hWnd, TIMER_ANIMATION_RECT);
    }
    m_pAnimationItem = NULL;
    KillTimer(m_hWnd, TIMER_ANIMATION_RECT);
    UINT uElpase = 50;
    m_nAninationDuration = 200;
    m_nAninationStep = m_nAninationDuration / uElpase;
    m_pAnimationItem = pItem;
    m_rcAnimation.left = pItem->rect.left + (pItem->rect.right - pItem->rect.left) / 2.0f;
    m_rcAnimation.right = m_rcAnimation.left;
    m_rcAnimation.top = pItem->rect.top + (pItem->rect.bottom - pItem->rect.top) / 2.0f;
    m_rcAnimation.bottom = m_rcAnimation.top;
    m_rcAninationStep.left = (pItem->rect.left - m_rcAnimation.left) / (float)m_nAninationStep;
    m_rcAninationStep.right = (pItem->rect.right - m_rcAnimation.right) / (float)m_nAninationStep;
    m_rcAninationStep.top = (pItem->rect.top - m_rcAnimation.top) / (float)m_nAninationStep;
    m_rcAninationStep.bottom = (pItem->rect.bottom - m_rcAnimation.bottom) / (float)m_nAninationStep;
    SetTimer(m_hWnd, TIMER_ANIMATION_RECT, uElpase, NULL);
}
double CEqsGraphWnd::PointToSegDist(double x, double y, double x1, double y1, double x2, double y2)
{
    double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
    if (cross <= 0) return sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
    double d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
    if (cross >= d2) return sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
    double r = cross / d2;
    double px = x1 + (x2 - x1) * r;
    double py = y1 + (y2 - y1) * r;
    return sqrt((x - px) * (x - px) + (py - y) * (py - y));
}
SourceCode/Bond/Servo/EqsGraphWnd.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,234 @@
#pragma once
#include <functional>
#ifndef EQSGRAPHWND_TAG
#ifdef _WIN32
#define EQSGRAPHWND_CLASSA        "EqsGraphWnd"
#define EQSGRAPHWND_CLASSW        L"EqsGraphWnd"
#ifdef UNICODE
#define EQSGRAPHWND_CLASS        EQSGRAPHWND_CLASSW
#else
#define EQSGRAPHWND_CLASS        EQSGRAPHWND_CLASSA
#endif
#else
#define EQSGRAPHWND_CLASS      "EqsGraphWnd"
#endif
#define EQSGRAPHWND_TAG            _T("EQSGRAPHWND_TAG")
#define EQSGRAPHWND_FIRST        (0U-2890U)
#define EQSGRAPHWND_LAST        (0U-2850U)
#define EQSGRAPHWND_            (EQSGRAPHWND_FIRST - 1)
#ifndef MIN
#define MIN(X,Y) (((X)>(Y))?(Y):(X))
#endif
#ifndef MAX
#define MAX(X,Y) (((X)>(Y))?(X):(Y))
#endif
typedef struct tagEQSGRAPHWND_NMHDR
{
    NMHDR        nmhdr;
    DWORD        dwData;
    DWORD        dwData1;
    DWORD        dwData2;
} EQSGRAPHWND_NMHDR;
typedef struct tagEQITEM
{
    unsigned int id;
    RECT rect;
    char text[64];
    BOOL bHighlight;
    int nShowType;
    DWORD_PTR pData;
    DWORD_PTR pInPins;
    DWORD_PTR pOutPins;
    int nFlashFlag;
} EQITEM;
typedef struct tagPIN
{
    int nIndex;
    int nType;
    char text[64];
    BOOL bHighlight;
    EQITEM* pItem;
    tagPIN *pConnectedPin;
    POINT ptConnectedLine[6];
    int nLinePtCount;
    DWORD_PTR pData;
} PIN;
#define ITEM_NORMAL        0
#define ITEM_SMALL        1
#define ITEM_LARGE        2
#endif
typedef std::function<bool(PIN *pPin1, PIN *pPin2)> ONCONNECTPIN;
typedef std::function<bool(PIN *pPin)> ONDISCONNECTPIN;
typedef std::function<bool(EQITEM* pItem)> ONDELETEEQITEM;
typedef std::function<void(EQITEM* pItem, int x, int y)> ONEQITEMPOSCHANGED;
typedef struct _EqsGraphListener
{
    ONCONNECTPIN            onConnectPin;
    ONCONNECTPIN            onCheckConnectPin;
    ONDISCONNECTPIN            onDisconnectPin;
    ONDELETEEQITEM            onDeleteEqItem;
    ONEQITEMPOSCHANGED        onEqItemPosChanged;
    ONDELETEEQITEM            onDblckEqItem;
    ONDELETEEQITEM            onRclickEqItem;
} EqsGraphListener;
class CEqsGraphWnd
{
    typedef struct tagRECTF
    {
        float    left;
        float    top;
        float    right;
        float    bottom;
    } RECTF;
public:
    CEqsGraphWnd();
    ~CEqsGraphWnd();
public:
    static BOOL RegisterWndClass();
    static CEqsGraphWnd* FromHandle(HWND hWnd);
    void SetFrameColor(COLORREF color);
    void SetBkgndColor(COLORREF color);
public:
    void EnableScroll(BOOL bEnable);
    void EnableMultiSelect();
    void SetItemRound(int nRound);
    void SetDefaultItemBackgroundColor(COLORREF crNormal, COLORREF crSel);
    void SetDefaultItemFrameColor(COLORREF crNormal, COLORREF crSel);
    void SetDefaultItemTextColor(COLORREF crNormal, COLORREF crSel);
    void SetOnListener(EqsGraphListener& listener);
    BOOL SetCurSel(int nSel);
    BOOL SetCurSel(CString strItemName);
    BOOL SetCurSel(DWORD_PTR pData);
    EQITEM * AddItem(int id, CString strText, DWORD_PTR dwData, int nType = ITEM_NORMAL);
    void RemoveItem(EQITEM* pItem);
    PIN * AddPin(EQITEM* pItem, int nType, CString strName, DWORD_PTR dwData);
    int DeleteItem(EQITEM* pItem);
    void DeleteAllItems();
    void SetItemSelectState(int nIndex, BOOL bSelect);
    void SetCurrentItem(EQITEM* pItem);
    BOOL GetItemRect(EQITEM* pItem, LPRECT lpRect);
    BOOL GetItemWarperRect(EQITEM* pItem, LPRECT lpRect);
    BOOL GetInPinRect(EQITEM* pItem, int nPinIndex, LPRECT lpRect);
    BOOL GetOutPinRect(EQITEM* pItem, int nPinIndex, LPRECT lpRect);
    BOOL GetPinPoint(PIN *pPin, LPPOINT lpPoint);
    EQITEM* GetItem(DWORD_PTR dwData);
    PIN * GetPin(DWORD_PTR dwItemData, DWORD_PTR dwPinData);
    int ConnectPin(DWORD_PTR dwItem1Data, DWORD_PTR dwPin1Data, DWORD_PTR dwItem2Data, DWORD_PTR dwPin2Data);
    void SetItemText(EQITEM* pItem, CString strText);
    void SetItemType(EQITEM* pItem, int type);
    void FlashItem(EQITEM* pItem);
    void AnimationItem(EQITEM*pItem);
private:
    void Init();
    void InitFont();
    void Notify(int nCode, int dwData, int dwData1 = 0, int dwData2 = 0);
    void Release();
    void ReleaseItem(EQITEM* pItem);
    void ReleaseAllItems();
    void CalculateScollbar();
    void CalculateMapPos();
    void CalculateMagneticLine(EQITEM* pItem, LPRECT lprcItemRect, int &hoz, int &ver);
    int HighTest(POINT pt, OUT EQITEM*& pItem, OUT PIN *& pPin);
    int GetPinState(PIN *pPin);
    void ClearConnectedLinePoint(EQITEM*& pItem);
    void DrawMagneticLine(LPRECT lprcClient, int nHozLine1, int nHozLine2, int nVerLine1, int nVerLine2);
    void DrawArrow(Gdiplus::Graphics *pGraphics, Gdiplus::Brush* pBrush, Gdiplus::Pen *pPen,
        int x, int y, int nArrowLen);
    void DrawDropItemRectangle(LPRECT lpRect1, LPRECT lpRect2);
    void DrawPinConnectedLine(Gdiplus::Graphics *pGraphics, Gdiplus::Brush *pBrush, Gdiplus::Pen *pPen, LPPOINT lpPt1, LPPOINT lpPt2,
        LPRECT lpRect1, LPRECT lpRect2, PIN *pOwnerPin);
    void DrawPinWillConnectLine(COLORREF color, LPPOINT lpPt1, LPPOINT lpPt2);
    double PointToSegDist(double x, double y, double x1, double y1, double x2, double y2);
    static CEqsGraphWnd* Hook(HWND hWnd);
    static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    static LRESULT OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
    LRESULT OnDestroy(WPARAM wParam, LPARAM lParam);
    LRESULT OnTimer(WPARAM wParam, LPARAM lParam);
    LRESULT OnNcPaint(WPARAM wParam, LPARAM lParam);
    LRESULT OnPaint(WPARAM wParam, LPARAM lParam);
    LRESULT OnMouseMove(WPARAM wParam, LPARAM lParam);
    LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam);
    LRESULT OnRButtonDown(WPARAM wParam, LPARAM lParam);
    LRESULT OnLButtonDblclk(WPARAM wParam, LPARAM lParam);
    LRESULT OnMouseWheel(WPARAM wParam, LPARAM lParam);
    LRESULT OnMouseHWheel(WPARAM wParam, LPARAM lParam);
    LRESULT OnKeyDown(WPARAM wParam, LPARAM lParam);
    LRESULT OnSize(WPARAM wParam, LPARAM lParam);
    LRESULT OnVScroll(WPARAM wParam, LPARAM lParam);
    LRESULT OnHScroll(WPARAM wParam, LPARAM lParam);
    LRESULT OnNitify(WPARAM wParam, LPARAM lParam);
private:
    EQITEM*        m_pCurItem;
    int            m_nFlashCount;
    EQITEM*        m_pFlashItem;
    EQITEM*        m_pAnimationItem;
    PIN *        m_pCurPin;
    PIN *        m_pSelLineOutPin;        // é€‰ä¸­çš„连线的两个pin中的out pin
private:
    HWND        m_hWnd;
    COLORREF    m_crBkgnd;
    COLORREF    m_crFrame;
    HFONT        m_hFontTitle;
private:
    BOOL m_bUseGdiPlus;                    // ä½¿ç”¨GDI+绘图?
    COLORREF m_crItemBackground[2];        // item的颜色,normal, active
    COLORREF m_crItemFrame[2];            // item的边框,normal, active
    COLORREF m_crItemNameText[2];
    COLORREF m_crItemIdText[2];
    COLORREF m_crPinBkgnd[3];            // pin的颜色,normal, active, enable connect
    int m_nCurSel;
    EqsGraphListener m_listener;
    CPtrArray m_arItem;
    BOOL m_bMultiSelect;
    int m_nItemRound;
private:
    int m_nStageCx;            // ç”»å¸ƒå¤§å°
    int m_nStageCy;
    int m_nOffsetX;
    int m_nOffsetY;
    // åŠ¨ç”»
    RECTF m_rcAnimation;
    RECTF m_rcAninationStep;
    int m_nAninationStep;
    int m_nAninationDuration;        // ms
    // å­—体
    HFONT m_hFontName;
    HFONT m_hFontId;
private:
    HWND m_hWndMapPos;
    BOOL m_bEnableScroll;
    int m_nMagneticLinHoz;
    int m_nMagneticLinVer;
};
SourceCode/Bond/Servo/HmTab.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,529 @@
// HmTab.cpp: implementation of the CHmTab class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "HmTab.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CHmTab::CHmTab()
{
    m_hWnd = NULL;
    m_crBkgnd = RGB(250, 250, 255);
    m_nPaddingLeft = 28;
    m_nPaddingBottom = 12;
    m_nItemMarginLeft = 8;
    m_crText[0] = RGB(88, 88, 88);
    m_crText[1] = RGB(18, 18, 18);
    m_pPressItem = nullptr;
    m_pHighItem = nullptr;
    LOGBRUSH lb;
    lb.lbColor = RGB(225, 127, 39);
    lb.lbHatch = 0;
    lb.lbStyle = BS_SOLID;
    DWORD iStyle = PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_SQUARE | PS_JOIN_MITER;
    m_hPenUnder[0] = ExtCreatePen(iStyle, 3, &lb, 0, NULL);
    lb.lbColor = RGB(225, 127, 39);
    m_hPenUnder[1] = ExtCreatePen(iStyle, 2, &lb, 0, NULL);
    lb.lbColor = RGB(223, 226, 230);
    m_hPenUnderWnd = ExtCreatePen(iStyle, 1, &lb, 0, NULL);
    m_nCurSel = 0;
}
CHmTab::~CHmTab()
{
    if (m_hPenUnder[0] != nullptr) {
        ::DeleteObject(m_hPenUnder[0]);
    }
    if (m_hPenUnder[1] != nullptr) {
        ::DeleteObject(m_hPenUnder[1]);
    }
    if (m_hPenUnderWnd != nullptr) {
        ::DeleteObject(m_hPenUnderWnd);
    }
}
BOOL CHmTab::RegisterWndClass()
{
    WNDCLASS wc;
    wc.lpszClassName = BYHMTAB_CLASS;
    wc.hInstance = AfxGetInstanceHandle();
    wc.lpfnWndProc = WindowProc;
    wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = 0;
    wc.lpszMenuName = NULL;
    wc.hbrBackground = NULL;
    wc.style = CS_GLOBALCLASS|CS_DBLCLKS;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    // æ³¨å†Œè‡ªå®šä¹‰ç±»
    return (::RegisterClass(&wc) != 0);
}
CHmTab* CHmTab::Hook(HWND hWnd)
{
    CHmTab* pHmTab = (CHmTab*)GetProp(hWnd, BYSTAG_HMTAB);
    if(pHmTab == NULL)
    {
        pHmTab = new CHmTab;
        pHmTab->m_hWnd = hWnd;
        SetProp(hWnd, BYSTAG_HMTAB, (HANDLE)pHmTab);
    }
    return pHmTab;
}
void CHmTab::Release()
{
    // delete
    delete this;
}
void CHmTab::SetPaddingLeft(int value)
{
    m_nPaddingLeft = value;
}
void CHmTab::SetItemMarginLeft(int value)
{
    m_nItemMarginLeft = value;
}
void CHmTab::SetTextColor(COLORREF color1, COLORREF color2)
{
    m_crText[0] = color1;
    m_crText[1] = color2;
    InvalidateRect(m_hWnd, NULL, TRUE);
}
int CHmTab::AddItem(const char* pszText,BOOL bUpdate/* = TRUE*/)
{
    HMTABITEM item;
    memset(&item, 0, sizeof(HMTABITEM));
    strcpy_s(item.szText, MHITEM_TEXT_MAX, pszText);
    m_items.push_back(item);
    if (bUpdate) {
        InvalidateRect(m_hWnd, NULL, TRUE);
    }
    return 0;
}
int CHmTab::DeleteItem(const char* pszText, BOOL bUpdate/* = TRUE*/)
{
    for (auto iter = m_items.begin(); iter != m_items.end(); iter++) {
        if (strcmp((*iter).szText, pszText) == 0) {
            m_items.erase(iter);
            break;
        }
    }
    if (bUpdate) {
        InvalidateRect(m_hWnd, NULL, TRUE);
    }
    return 0;
}
void CHmTab::Notify(int nCode, DWORD_PTR dwData, DWORD_PTR dwData1/* = 0*/, DWORD_PTR dwData2/* = 0*/)
{
    HWND hParent;
    hParent = GetParent(m_hWnd);
    if (hParent != NULL) {
        BYHMTAB_NMHDR iii_nmhdr;
        iii_nmhdr.nmhdr.hwndFrom = m_hWnd;
        iii_nmhdr.nmhdr.idFrom = GetWindowLong(m_hWnd, GWL_ID);
        iii_nmhdr.nmhdr.code = nCode;
        iii_nmhdr.dwData = dwData;
        iii_nmhdr.dwData1 = dwData1;
        iii_nmhdr.dwData2 = dwData2;
        SendMessage(hParent, WM_NOTIFY, (WPARAM)iii_nmhdr.nmhdr.idFrom, (LPARAM)&iii_nmhdr);
    }
}
////////////////////////////////
// æ‹¦æˆªçª—口消息函数
LRESULT CALLBACK CHmTab::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHmTab* pHmTab = (CHmTab *)GetProp(hWnd, BYSTAG_HMTAB);
    if(pHmTab == NULL && uMsg != WM_NCCREATE)
    {
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    // å¦‚æžœHook则响应消息
    ASSERT(hWnd);
    switch(uMsg)
    {
    case WM_NCCREATE:
        return OnNcCreate(hWnd, wParam, lParam);
    case WM_DESTROY:
        return pHmTab->OnDestroy(wParam, lParam);
    case WM_PAINT:
        return pHmTab->OnPaint(wParam, lParam);
    case WM_TIMER:
        return pHmTab->OnTimer(wParam, lParam);
    case WM_MOUSEMOVE:
        return pHmTab->OnMouseMove(wParam, lParam);
    case WM_LBUTTONDOWN:
        return pHmTab->OnLButtonDown(wParam, lParam);
    case WM_SETCURSOR:
        return pHmTab->OnSetCursor(wParam, lParam);
    case WM_SIZE:
        return pHmTab->OnSize(wParam, lParam);
    case WM_GETDLGCODE:
        return DLGC_WANTALLKEYS;
    default:
        break;
    }
    return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
///////////////////////////////
// WM_NCCREATE
// çª—口创建前的初始化工作
LRESULT CHmTab::OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    CHmTab* pHmTab = (CHmTab *)GetProp(hWnd, BYSTAG_HMTAB);
    ASSERT(pHmTab == NULL);
    Hook(hWnd);
    return ::DefWindowProc(hWnd, WM_NCCREATE, wParam, lParam);
}
///////////////////////////////
// WM_DESTROY
LRESULT CHmTab::OnDestroy(WPARAM wParam, LPARAM lParam)
{
    Release();
    return ::DefWindowProc(m_hWnd, WM_DESTROY, wParam, lParam);
}
///////////////////////////////
// WM_TIMER
LRESULT CHmTab::OnTimer(WPARAM wParam, LPARAM lParam)
{
    if (wParam == 1) {
        POINT pt;
        GetCursorPos(&pt);
        ::ScreenToClient(m_hWnd, &pt);
        HMTABITEM* pLastHighItem = m_pHighItem;
        HighTest(pt, m_pHighItem, nullptr);
        if (m_pHighItem != pLastHighItem) {
            ::InvalidateRect(m_hWnd, NULL, TRUE);
        }
        if (m_pHighItem == nullptr) {
            ::KillTimer(m_hWnd, 1);
        }
    }
    return ::DefWindowProc(m_hWnd, WM_TIMER, wParam, lParam);
}
///////////////////////////////
// WM_MOUSEMOVE
LRESULT CHmTab::OnMouseMove(WPARAM wParam, LPARAM lParam)
{
    POINT pt;
    pt.x = LOWORD(lParam);
    pt.y = HIWORD(lParam);
    HMTABITEM* pLastHighItem = m_pHighItem;
    int nHitCode = HighTest(pt, m_pHighItem, nullptr);
    if (m_pHighItem != pLastHighItem) {
        RECT rcClient;
        GetClientRect(m_hWnd, &rcClient);
        ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    }
    if (m_pHighItem != NULL) {
        ::KillTimer(m_hWnd, 1);
        ::SetTimer(m_hWnd, 1, 100, NULL);
    }
    ::SetProp(m_hWnd, HMTAB_HITCODETEST, (HANDLE)(__int64)nHitCode);
    return ::DefWindowProc(m_hWnd, WM_MOUSEMOVE, wParam, lParam);
}
/*
 * WM_LBUTTONDOWN
 * é¼ æ ‡å·¦é”®æŒ‰ä¸‹
 */
LRESULT CHmTab::OnLButtonDown(WPARAM wParam, LPARAM lParam)
{
    POINT pt, ptNew;
    pt.x = LOWORD(lParam);
    pt.y = HIWORD(lParam);
    BOOL bButtonUp = FALSE;
    int nClickIndex = -1;
    HighTest(pt, m_pPressItem, &nClickIndex);
    if (m_pPressItem != NULL) {
        m_pHighItem = NULL;
        ::InvalidateRect(m_hWnd, NULL, TRUE);
    }
    // æ•捉鼠标消息,检测是否拖动
    HMTABITEM* pPressItem = NULL;
    if (m_pPressItem != NULL) {
        ::KillTimer(m_hWnd, 1);
        if (::GetCapture() == NULL) {
            SetCapture(m_hWnd);
            ASSERT(m_hWnd == GetCapture());
            AfxLockTempMaps();
            for (;;)
            {
                MSG msg;
                VERIFY(::GetMessage(&msg, NULL, 0, 0));
                if (GetCapture() != m_hWnd) break;
                switch (msg.message)
                {
                case WM_MOUSEMOVE:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    HighTest(ptNew, pPressItem, &nClickIndex);
                    if (pPressItem != m_pPressItem) {
                        m_pPressItem = nullptr;
                        ::InvalidateRect(m_hWnd, NULL, TRUE);
                    }
                    break;
                case WM_LBUTTONUP:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    HighTest(ptNew, pPressItem, &nClickIndex);
                    if (m_pPressItem != nullptr && pPressItem == m_pPressItem) {
                        m_nCurSel = nClickIndex;
                        bButtonUp = TRUE;
                    }
                    goto ExitLoop;
                case WM_KEYDOWN:
                    if (msg.wParam == VK_ESCAPE) {
                        goto ExitLoop;
                    }
                    break;
                default:
                    DispatchMessage(&msg);
                    break;
                }
            }
        ExitLoop:
            m_pPressItem = NULL;
            ReleaseCapture();
            ::InvalidateRect(m_hWnd, NULL, TRUE);
            if (bButtonUp) {
                Notify((int)BYHMTAB_SEL_CHANGED, m_nCurSel);
            }
            AfxUnlockTempMaps(FALSE);
        }
    }
    return ::DefWindowProc(m_hWnd, WM_LBUTTONDOWN, wParam, lParam);
}
///////////////////////////////
// WM_SETCURSOR
LRESULT CHmTab::OnSetCursor(WPARAM wParam, LPARAM lParam)
{
    int nHitCode = (int)(__int64)GetProp(m_hWnd, HMTAB_HITCODETEST);
    switch (nHitCode)
    {
    case HMTAB_HT_NOWHERE:
    case HMTAB_HT_ITEM:
        SetCursor(::LoadCursor(NULL, IDC_ARROW));
        return TRUE;
    case HMTAB_HT_HIGT_ITEM:
        SetCursor(::LoadCursor(NULL, IDC_HAND));
        return TRUE;
    default:
        break;
    }
    return ::DefWindowProc(m_hWnd, WM_SETCURSOR, wParam, lParam);
}
/*
 * WM_SIZE
 */
LRESULT CHmTab::OnSize(WPARAM wParam, LPARAM lParam)
{
    ::InvalidateRect(m_hWnd, NULL, TRUE);
    return ::DefWindowProc(m_hWnd, WM_SIZE, wParam, lParam);
}
///////////////////////////////
// WM_PAINT
LRESULT CHmTab::OnPaint(WPARAM wParam, LPARAM lParam)
{
    HDC hDC, hMemDC;
    HBITMAP hBitmap;
    RECT rcClient;
    CString strText;
    HFONT hFont1, hFont2;
    HBRUSH hBrushBK;
    // BeginPaint
    PAINTSTRUCT ps;
    hDC = BeginPaint(m_hWnd, &ps);
    GetClientRect(m_hWnd, &rcClient);
    hMemDC = ::CreateCompatibleDC(hDC);
    hBitmap = ::CreateCompatibleBitmap(hDC, rcClient.right - rcClient.left,
        rcClient.bottom - rcClient.top);
    ::SelectObject(hMemDC, hBitmap);
    ::SetBkMode(hMemDC, TRANSPARENT);
    HFONT hFontDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
    CFont* pFont = CFont::FromHandle(hFontDefault);
    LOGFONT lf;
    pFont->GetLogFont(&lf);
    hFont1 = ::CreateFontIndirect(&lf);
    lf.lfHeight -= 5;
    hFont2 = ::CreateFontIndirect(&lf);
    // èƒŒæ™¯é¢œè‰²
    hBrushBK = CreateSolidBrush(m_crBkgnd);
    ::FillRect(hMemDC, &rcClient, hBrushBK);
    DeleteObject(hBrushBK);
    SelectObject(hMemDC, m_hPenUnderWnd);
    ::MoveToEx(hMemDC, rcClient.left, rcClient.bottom - 2, NULL);
    ::LineTo(hMemDC, rcClient.right, rcClient.bottom - 2);
    // ç»˜åˆ¶å­é¡¹
    SIZE sizeItem;
    RECT rcItem, rcText;
    rcItem.top = rcClient.top;
    rcItem.bottom = rcClient.bottom-1;
    rcItem.left = m_nPaddingLeft;
    int index = 0;
    for (int i = 0; i < m_items.size(); i++) {
        auto& item = m_items.at(i);
        ::SelectObject(hMemDC, i == m_nCurSel ? hFont2 : hFont1);
        ::SetTextColor(hMemDC, i == m_nCurSel ? m_crText[1] : m_crText[0]);
        ::GetTextExtentPoint32(hMemDC, item.szText, (int)strlen(item.szText), &sizeItem);
        rcItem.left += m_nItemMarginLeft;
        rcItem.right = rcItem.left + (int)sizeItem.cx;
        ::CopyRect(&rcText, &rcItem);
        rcText.bottom -= m_nPaddingBottom;
        ::DrawText(hMemDC, item.szText, (int)strlen(item.szText), &rcText,
            DT_LEFT | DT_BOTTOM | DT_SINGLELINE | DT_END_ELLIPSIS);
        ::CopyRect(&item.rect, &rcItem);
        // ä¸‹åˆ’线?
        if (i == m_nCurSel || &item == m_pHighItem) {
            HPEN hOldPen = (HPEN)::SelectObject(hMemDC, i == m_nCurSel ? m_hPenUnder[0] : m_hPenUnder[1]);
            ::MoveToEx(hMemDC, item.rect.left, item.rect.bottom - 3, NULL);
            ::LineTo(hMemDC, item.rect.right, item.rect.bottom - 3);
            ::SelectObject(hMemDC, hOldPen);
        }
        index++;
        rcItem.left = rcItem.right;
    }
    ::DeleteObject(hFont1);
    ::DeleteObject(hFont2);
    // EndPaint
    ::BitBlt(hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
        hMemDC, 0, 0, SRCCOPY);
    EndPaint(m_hWnd, &ps);
    ::DeleteObject(hBitmap);
    ::DeleteDC(hMemDC);
    return 1;
}
void CHmTab::SetBkgndColor(COLORREF cr)
{
    m_crBkgnd = cr;
}
/*
 * æ£€æµ‹åæ ‡ç‚¹æ‰€åœ¨çš„项
 * è¿”回, TYGTLITEM
 */
int CHmTab::HighTest(POINT pt, OUT HMTABITEM*& pItem, int* pnIndex)
{
    // æ£€æµ‹æ˜¯å¦åœ¨æŸä¸ªå­é¡¹
    int nRet = HMTAB_HT_NOWHERE;
    pItem = NULL;
    for (int i = 0; i < m_items.size(); i++) {
        auto& item = m_items.at(i);
        if (::PtInRect(&item.rect, pt)) {
            pItem = &item;
            nRet = pItem == m_pHighItem ? HMTAB_HT_HIGT_ITEM : HMTAB_HT_ITEM;
            if (pnIndex != nullptr) *pnIndex = i;
            break;
        }
    }
    return nRet;
}
int CHmTab::GetCurSel()
{
    return m_nCurSel;
}
void CHmTab::SetCurSel(int index)
{
    if (0 <= index && index <= m_items.size()) {
        m_nCurSel = index;
        InvalidateRect(m_hWnd, NULL, TRUE);
    }
}
int CHmTab::GetItemCount()
{
    return (int)m_items.size();
}
SourceCode/Bond/Servo/HmTab.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,130 @@
// HmTab.h: interface for the CHmTab class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_HMTAB_H__FBB8916A_DE77_4EA3_9C21_E51E6B06194C__INCLUDED_)
#define AFX_HMTAB_H__FBB8916A_DE77_4EA3_9C21_E51E6B06194C__INCLUDED_
#pragma comment(lib, "Msimg32.lib")            // TransparentBlt
#include <vector>
//====== HmTab =====================================================
#ifndef NOHMTAB
#ifdef _WIN32
#define BYHMTAB_CLASSA       "BYHmTab"
#define BYHMTAB_CLASSW       L"BYHmTab"
#ifdef UNICODE
#define  BYHMTAB_CLASS       BYHMTAB_CLASSW
#else
#define  BYHMTAB_CLASS       BYHMTAB_CLASSA
#endif
#else
#define BYHMTAB_CLASS        "BYHmTab"
#endif
#define BYSTAG_HMTAB         _T("ISHMTAB")
//====== WM_NOTIFY codes (NMHDR.code values) ==================================
#define BYHMTAB_FIRST                 (0U-1190U)       //
#define BYHMTAB_LAST                 (0U-1150U)
#define BYHMTAB_SEL_CHANGED              (BYHMTAB_FIRST - 1)
typedef struct tagBYHMTAB_NMHDR
{
    NMHDR        nmhdr;
    DWORD_PTR    dwData;
    DWORD_PTR    dwData1;
    DWORD_PTR    dwData2;
} BYHMTAB_NMHDR;
#define MHITEM_TEXT_MAX        64
typedef struct tagHMTABITEM
{
    int id;
    char szText[MHITEM_TEXT_MAX];
    RECT rect;
} HMTABITEM;
#define HMTAB_HITCODETEST            _T("HitCode")
#define HMTAB_HT_NOWHERE            0x1
#define HMTAB_HT_ITEM                0x2
#define HMTAB_HT_HIGT_ITEM            0x4
#endif
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CHmTab
{
public:
    CHmTab();
    virtual ~CHmTab();
public:
    static BOOL RegisterWndClass();
    static CHmTab* Hook(HWND hWnd);
    void Notify(int nCode, DWORD_PTR dwData, DWORD_PTR dwData1 = 0, DWORD_PTR dwData2 = 0);
    void Release();
    void SetBkgndColor(COLORREF cr);
    static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    static LRESULT OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
    LRESULT OnDestroy(WPARAM wParam, LPARAM lParam);
    LRESULT OnTimer(WPARAM wParam, LPARAM lParam);
    LRESULT OnPaint(WPARAM wParam, LPARAM lParam);
    LRESULT OnSetCursor(WPARAM wParam, LPARAM lParam);
    LRESULT OnSize(WPARAM wParam, LPARAM lParam);
    LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam);
    LRESULT OnMouseMove(WPARAM wParam, LPARAM lParam);
public:
    void SetPaddingLeft(int value);
    void SetItemMarginLeft(int value);
    void SetTextColor(COLORREF color1, COLORREF color2);
    int AddItem(const char* pszText, BOOL bUpdate = TRUE);
    int DeleteItem(const char* pszText, BOOL bUpdate = TRUE);
    int HighTest(POINT pt, OUT HMTABITEM*& pItem, int* pnIndex);
    int GetCurSel();
    void SetCurSel(int index);
    int GetItemCount();
private:
    HWND        m_hWnd;
    COLORREF m_crBkgnd;
private:
    int m_nCurSel;
    std::vector< HMTABITEM > m_items;
    HMTABITEM* m_pHighItem;
    HMTABITEM* m_pPressItem;
    int m_nPaddingLeft;
    int m_nPaddingBottom;
    int m_nItemMarginLeft;
    COLORREF m_crText[2];
    HPEN m_hPenUnder[2];
    HPEN m_hPenUnderWnd;
};
#endif // !defined(AFX_HMTAB_H__FBB8916A_DE77_4EA3_9C21_E51E6B06194C__INCLUDED_)
SourceCode/Bond/Servo/MapPosWnd.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,536 @@
#include "stdafx.h"
#include "MapPosWnd.h"
#include "ColorTransfer.h"
#define HT_NOWHERE        0x1
#define HT_INDICATOR    0x2
CMapPosWnd::CMapPosWnd()
{
    m_hWnd = NULL;
    m_crFrame = RGB(0, 0, 0);
    m_crBkgnd = RGB(255, 255, 255);
    m_crViewPort = RGB(185, 122, 87);
    m_nWndMaxSize = 200;
    m_nStageCx = 4000;
    m_nStageCy = 3000;
    m_rcViewPort = {200, 200, 800, 800};
}
CMapPosWnd::~CMapPosWnd()
{
}
BOOL CMapPosWnd::RegisterWndClass()
{
    WNDCLASS wc;
    wc.lpszClassName = MAPPOSWND_CLASS;
    wc.hInstance = AfxGetInstanceHandle();
    wc.lpfnWndProc = WindowProc;
    wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = 0;
    wc.lpszMenuName = NULL;
    wc.hbrBackground = NULL;
    wc.style = CS_GLOBALCLASS | CS_DBLCLKS;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    // æ³¨å†Œçª—口类
    return (::RegisterClass(&wc) != 0);
}
CMapPosWnd * CMapPosWnd::FromHandle(HWND hWnd)
{
    CMapPosWnd *pMapPosWnd = (CMapPosWnd *)::GetProp(hWnd, MAPPOSWND_TAG);
    return pMapPosWnd;
}
CMapPosWnd* CMapPosWnd::Hook(HWND hWnd)
{
    CMapPosWnd* pMapPosWnd = (CMapPosWnd*)GetProp(hWnd, MAPPOSWND_TAG);
    if (pMapPosWnd == NULL) {
        pMapPosWnd = new CMapPosWnd();
        pMapPosWnd->m_hWnd = hWnd;
        SetProp(hWnd, MAPPOSWND_TAG, (HANDLE)pMapPosWnd);
    }
    return pMapPosWnd;
}
void CMapPosWnd::SetWndMaxSize(int nMaxSize)
{
    m_nWndMaxSize = nMaxSize;
}
void CMapPosWnd::SetStageSize(int cx, int cy, BOOL bInvalidata)
{
    m_nStageCx = cx;
    m_nStageCy = cy;
    float scale = max(m_nStageCx / (float)m_nWndMaxSize, m_nStageCy / (float)m_nWndMaxSize);
    int w = (int)(m_nStageCx / scale) +1;
    int h = (int)(m_nStageCy / scale) + 1;
    ::SetWindowPos(m_hWnd, NULL, 0, 0, w, h, SWP_NOMOVE);
    if (bInvalidata) {
        RECT rcClient;
        GetClientRect(m_hWnd, &rcClient);
        InvalidateRect(m_hWnd, &rcClient, TRUE);
    }
}
void CMapPosWnd::SetViewPort(LPRECT lpRect, BOOL bInvalidata)
{
    ::CopyRect(&m_rcViewPort, lpRect);
    if (bInvalidata) {
        RECT rcClient;
        GetClientRect(m_hWnd, &rcClient);
        InvalidateRect(m_hWnd, &rcClient, TRUE);
    }
}
void CMapPosWnd::GetViewPortRect(LPRECT lprcClient, LPRECT lprcDest)
{
    RECT rcClient;
    if (lprcClient == NULL) {
        GetClientRect(m_hWnd, &rcClient);
        lprcClient = &rcClient;
    }
    float scale = max(m_nStageCx / (float)(lprcClient->right - lprcClient->left), m_nStageCy / (float)(lprcClient->bottom - lprcClient->top));
    lprcDest->left = long(m_rcViewPort.left / scale);
    lprcDest->top = long(m_rcViewPort.top / scale);
    lprcDest->right = long(m_rcViewPort.right / scale);
    lprcDest->bottom = long(m_rcViewPort.bottom / scale);
}
void CMapPosWnd::Init()
{
}
void CMapPosWnd::Release()
{
    // delete
    delete this;
}
void CMapPosWnd::Notify(int nCode, int dwData, int dwData1/* = 0*/, int dwData2/* = 0*/)
{
    HWND hParent;
    hParent = GetParent(m_hWnd);
    if (hParent != NULL) {
        MAPPOSWND_NMHDR nmhdr;
        nmhdr.nmhdr.hwndFrom = m_hWnd;
        nmhdr.nmhdr.idFrom = GetWindowLong(m_hWnd, GWL_ID);
        nmhdr.nmhdr.code = nCode;
        nmhdr.dwData = dwData;
        nmhdr.dwData1 = dwData1;
        nmhdr.dwData2 = dwData2;
        SendMessage(hParent, WM_NOTIFY, (WPARAM)nmhdr.nmhdr.idFrom, (LPARAM)&nmhdr);
    }
}
/*
 * æ£€æµ‹åæ ‡ç‚¹æ‰€åœ¨çš„项
 * è¿”回项类型, å¦‚HT_INDICATOR
 */
int CMapPosWnd::HighTest(POINT pt)
{
    // æ£€æµ‹æ˜¯å¦åœ¨æŸä¸ªå­é¡¹
    int nRet = HT_NOWHERE;
    RECT rcClient, rcViewPort;
    GetClientRect(m_hWnd, &rcClient);
    GetViewPortRect(&rcClient, &rcViewPort);
    if (::PtInRect(&rcViewPort, pt)) {
        nRet = HT_INDICATOR;
    }
    return nRet;
}
/*
 * WindowProc,窗口过程
 */
LRESULT CALLBACK CMapPosWnd::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CMapPosWnd* pMapPosWnd = (CMapPosWnd *)GetProp(hWnd, MAPPOSWND_TAG);
    if (pMapPosWnd == NULL && uMsg != WM_NCCREATE)
    {
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    // å¤„理窗口消息
    ASSERT(hWnd);
    switch (uMsg)
    {
    case WM_NCCREATE:
        return CMapPosWnd::OnNcCreate(hWnd, wParam, lParam);
    case WM_DESTROY:
        return pMapPosWnd->OnDestroy(wParam, lParam);
    case WM_NCPAINT:
        return pMapPosWnd->OnNcPaint(wParam, lParam);
    case WM_PAINT:
        return pMapPosWnd->OnPaint(wParam, lParam);
    case WM_TIMER:
        return pMapPosWnd->OnTimer(wParam, lParam);
    case WM_MOUSEMOVE:
        return pMapPosWnd->OnMouseMove(wParam, lParam);
    case WM_LBUTTONDOWN:
        return pMapPosWnd->OnLButtonDown(wParam, lParam);
    case WM_LBUTTONDBLCLK:
        return pMapPosWnd->OnLButtonDblclk(wParam, lParam);
    case WM_MOUSEWHEEL:
        return pMapPosWnd->OnMouseWheel(wParam, lParam);
    case WM_KEYDOWN:
        return pMapPosWnd->OnKeyDown(wParam, lParam);
    case WM_SIZE:
        return pMapPosWnd->OnSize(wParam, lParam);
    case WM_GETDLGCODE:
        return DLGC_WANTALLKEYS;
    default:
        break;
    }
    return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
/*
 * WM_NCCREATE
 * çª—口创建
 */
LRESULT CMapPosWnd::OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    CMapPosWnd* pMapPosWnd = (CMapPosWnd *)GetProp(hWnd, MAPPOSWND_TAG);
    ASSERT(pMapPosWnd == NULL);
    Hook(hWnd)->Init();
    return ::DefWindowProc(hWnd, WM_NCCREATE, wParam, lParam);
}
/*
 * WM_DESTROY
 * çª—口销毁
 */
LRESULT CMapPosWnd::OnDestroy(WPARAM wParam, LPARAM lParam)
{
    Release();
    return ::DefWindowProc(m_hWnd, WM_DESTROY, wParam, lParam);
}
/*
 * WM_TIMER
 */
LRESULT CMapPosWnd::OnTimer(WPARAM wParam, LPARAM lParam)
{
    return ::DefWindowProc(m_hWnd, WM_TIMER, wParam, lParam);
}
/*
 * WM_MOUSEMOVE
 * é¼ æ ‡æ»šåЍ
 */
LRESULT CMapPosWnd::OnMouseMove(WPARAM wParam, LPARAM lParam)
{
    return ::DefWindowProc(m_hWnd, WM_MOUSEMOVE, wParam, lParam);
}
/*
 * WM_LBUTTONDOWN
 * é¼ æ ‡å·¦é”®æŒ‰ä¸‹
 */
LRESULT CMapPosWnd::OnLButtonDown(WPARAM wParam, LPARAM lParam)
{
    POINT pt, ptNew, ptPox;
    pt.x = LOWORD(lParam);
    pt.y = HIWORD(lParam);
    RECT rcClient, rcLastViewPort;
    GetClientRect(m_hWnd, &rcClient);
    float scale = max(m_nStageCx / (float)(rcClient.right - rcClient.left), m_nStageCy / (float)(rcClient.bottom - rcClient.top));
    CopyRect(&rcLastViewPort, &m_rcViewPort);
    // æ£€æµ‹ç‚¹å‡»åæ ‡æ˜¯å¦åœ¨æŸä¸€å­é¡¹ä¸Šï¼Œå¦‚是,则高亮显示
    int nRet = HighTest(pt);
    SetFocus(m_hWnd);
    // æ•捉鼠标消息,检测是否拖动
    if (nRet == HT_INDICATOR) {
        if (::GetCapture() == NULL) {
            SetCapture(m_hWnd);
            ASSERT(m_hWnd == GetCapture());
            AfxLockTempMaps();
            for (;;) {
                MSG msg;
                VERIFY(::GetMessage(&msg, NULL, 0, 0));
                if (GetCapture() != m_hWnd) break;
                switch (msg.message)
                {
                case WM_MOUSEMOVE:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    ptPox.x = long(rcLastViewPort.left + (ptNew.x - pt.x) * scale);
                    ptPox.x = max(0, min(ptPox.x, m_nStageCx - (rcLastViewPort.right - rcLastViewPort.left)));
                    ptPox.y = long(rcLastViewPort.top + (ptNew.y - pt.y) * scale);
                    ptPox.y = max(0, min(ptPox.y, m_nStageCy - (rcLastViewPort.bottom - rcLastViewPort.top)));
                    Notify(MAPPOSWND_POSCHANGED, ptPox.x, ptPox.y);
                    break;
                case WM_LBUTTONUP:
                    ptNew = msg.pt;
                    ::ScreenToClient(m_hWnd, &ptNew);
                    ptPox.x = long(rcLastViewPort.left + (ptNew.x - pt.x) * scale);
                    ptPox.x = max(0, min(ptPox.x, m_nStageCx - (rcLastViewPort.right - rcLastViewPort.left)));
                    ptPox.y = long(rcLastViewPort.top + (ptNew.y - pt.y) * scale);
                    ptPox.y = max(0, min(ptPox.y, m_nStageCy - (rcLastViewPort.bottom - rcLastViewPort.top)));
                    Notify(MAPPOSWND_POSCHANGED, ptPox.x, ptPox.y);
                    ReleaseCapture();
                    ::InvalidateRect(m_hWnd, &rcClient, TRUE);
                    goto ExitLoop;
                case WM_KEYDOWN:
                    if (msg.wParam != VK_ESCAPE)
                        break;
                default:
                    DispatchMessage(&msg);
                    break;
                }
            }
            ReleaseCapture();
        ExitLoop:
            AfxUnlockTempMaps(FALSE);
        }
    }
    return ::DefWindowProc(m_hWnd, WM_LBUTTONDOWN, wParam, lParam);
}
/*
 * WM_LBUTTONDBLCLK
 * é¼ æ ‡å·¦é”®åŒå‡»
 */
LRESULT CMapPosWnd::OnLButtonDblclk(WPARAM wParam, LPARAM lParam)
{
    POINT pt, ptDest;
    pt.x = LOWORD(lParam);
    pt.y = HIWORD(lParam);
    RECT rcClient, rcLast;
    GetClientRect(m_hWnd, &rcClient);
    rcLast = { 0, 0, 0, 0 };
    // æ£€æµ‹ç‚¹å‡»åæ ‡æ˜¯å¦åœ¨ç©ºç™½å¤„
    // å°çª—坐标转换为对应坐标
    int nRet = HighTest(pt);
    if (nRet == HT_NOWHERE || nRet == HT_INDICATOR) {
        float scale = max(m_nStageCx / (float)(rcClient.right - rcClient.left), m_nStageCy / (float)(rcClient.bottom - rcClient.top));
        ptDest.x = (int)(scale * pt.x) - (m_rcViewPort.right - m_rcViewPort.left) / 2;
        ptDest.x = max(0, min(ptDest.x, m_nStageCx - (m_rcViewPort.right - m_rcViewPort.left)));
        ptDest.y = (int)(scale * pt.y) - (m_rcViewPort.bottom - m_rcViewPort.top) / 2;
        ptDest.y = max(0, min(ptDest.y, m_nStageCy - (m_rcViewPort.bottom - m_rcViewPort.top)));
        Notify(MAPPOSWND_POSCHANGED, ptDest.x, ptDest.y);
    }
    return ::DefWindowProc(m_hWnd, WM_LBUTTONDBLCLK, wParam, lParam);
}
/*
 * WM_MOUSEWHEEL
 * é¼ æ ‡æ»šåЍ
 */
LRESULT CMapPosWnd::OnMouseWheel(WPARAM wParam, LPARAM lParam)
{
    return ::DefWindowProc(m_hWnd, WM_MOUSEWHEEL, wParam, lParam);
}
/*
 * WM_KEYDOWN
 * é”®ç›˜æ¶ˆæ¯ï¼ŒæŒ‰ä¸‹æŒ‰é”®
 */
LRESULT CMapPosWnd::OnKeyDown(WPARAM wParam, LPARAM lParam)
{
    BOOL bChanged = FALSE;
    if (wParam == VK_DELETE) {
    }
    if (bChanged) {
        RECT rcClient;
        GetClientRect(m_hWnd, &rcClient);
        ::InvalidateRect(m_hWnd, &rcClient, TRUE);
    }
    return ::DefWindowProc(m_hWnd, WM_KEYDOWN, wParam, lParam);
}
/*
 * WM_NCPAINT
 */
LRESULT CMapPosWnd::OnNcPaint(WPARAM wParam, LPARAM lParam)
{
    LRESULT lRet = ::DefWindowProc(m_hWnd, WM_NCPAINT, wParam, lParam);
    long styleEx = GetWindowLong(m_hWnd, GWL_EXSTYLE);
    if ((styleEx & WS_EX_CLIENTEDGE) == WS_EX_CLIENTEDGE) {
        RECT rect, rcClient;
        GetClientRect(m_hWnd, &rcClient);
        ::ClientToScreen(m_hWnd, (LPPOINT)&rcClient.left);
        ::ClientToScreen(m_hWnd, (LPPOINT)&rcClient.right);
        GetWindowRect(m_hWnd, &rect);
        rcClient.right = rect.right - 1;
        rcClient.bottom = rect.bottom - 1;
        ::OffsetRect(&rcClient, -rect.left, -rect.top);
        rect.right -= rect.left;
        rect.bottom -= rect.top;
        rect.left = 0;
        rect.top = 0;
        HRGN hRgnWnd = CreateRectRgnIndirect(&rect);
        HRGN hRgnClient = CreateRectRgnIndirect(&rcClient);
        HBRUSH hBrushBK, hBrushFrame;
        HDC hDC = ::GetWindowDC(m_hWnd);
        ::SelectClipRgn(hDC, hRgnWnd);
        ::ExtSelectClipRgn(hDC, hRgnClient, RGN_DIFF);
        hBrushBK = CreateSolidBrush(m_crBkgnd);
        ::FillRect(hDC, &rect, hBrushBK);
        DeleteObject(hBrushBK);
        hBrushFrame = CreateSolidBrush(m_crFrame);
        ::FrameRect(hDC, &rect, hBrushFrame);
        ::DeleteObject(hRgnWnd);
        ::DeleteObject(hRgnClient);
        DeleteObject(hBrushFrame);
        ::ReleaseDC(m_hWnd, hDC);
    }
    return lRet;
}
/*
 * WM_PAINT
 */
LRESULT CMapPosWnd::OnPaint(WPARAM wParam, LPARAM lParam)
{
    HDC hDC, hMemDC;
    HBITMAP hBitmap;
    RECT rcClient;
    CString strText;
    HBRUSH hBrushBK;
    // BeginPaint
    PAINTSTRUCT ps;
    hDC = BeginPaint(m_hWnd, &ps);
    GetClientRect(m_hWnd, &rcClient);
    hMemDC = ::CreateCompatibleDC(hDC);
    hBitmap = ::CreateCompatibleBitmap(hDC, rcClient.right - rcClient.left,
        rcClient.bottom - rcClient.top);
    ::SelectObject(hMemDC, hBitmap);
    // èƒŒæ™¯é¢œè‰²
    hBrushBK = CreateSolidBrush(m_crBkgnd);
    ::FillRect(hMemDC, &rcClient, hBrushBK);
    DeleteObject(hBrushBK);
    // æ ‡é¢˜
    {
        char szTitle[256];
        GetWindowText(m_hWnd, szTitle, 256);
        RECT rcTitle;
        rcTitle.left = rcClient.left + 2;
        rcTitle.top = rcClient.top + 2;
        rcTitle.bottom = rcClient.bottom - 2;
        rcTitle.right = rcClient.right - 2;
        ::DrawText(hMemDC, szTitle, (int)strlen(szTitle), &rcTitle, DT_LEFT | DT_TOP);
    }
    // View port
    RECT rcViewPort;
    GetViewPortRect(&rcClient, &rcViewPort);
    HBRUSH hBrushFrame = CreateSolidBrush(m_crViewPort);
    ::FrameRect(hMemDC, &rcViewPort, hBrushFrame);
    ::DeleteObject(hBrushFrame);
    // EndPaint
    ::BitBlt(hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
        hMemDC, 0, 0, SRCCOPY);
    EndPaint(m_hWnd, &ps);
    ::DeleteObject(hBitmap);
    ::DeleteDC(hMemDC);
    return 1;
}
/*
 * WM_SIZE
 */
LRESULT CMapPosWnd::OnSize(WPARAM wParam, LPARAM lParam)
{
    LRESULT lRet = ::DefWindowProc(m_hWnd, WM_SIZE, wParam, lParam);
    return lRet;
}
/*
 * è®¾ç½®èƒŒæ™¯é¢œè‰²
 * color -- èƒŒæ™¯è‰²
 */
void CMapPosWnd::SetBkgndColor(COLORREF color)
{
    m_crBkgnd = color;
}
/*
 * è¾¹æ¡†é¢œè‰²
 * color -- è¾¹æ¡†è‰²
 */
void CMapPosWnd::SetFrameColor(COLORREF color)
{
    m_crFrame = color;
}
SourceCode/Bond/Servo/MapPosWnd.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
#pragma once
#include <functional>
#ifndef MAPPOSWND_TAG
#ifdef _WIN32
#define MAPPOSWND_CLASSA        "MapPosWnd"
#define MAPPOSWND_CLASSW        L"MapPosWnd"
#ifdef UNICODE
#define MAPPOSWND_CLASS        MAPPOSWND_CLASSW
#else
#define MAPPOSWND_CLASS        MAPPOSWND_CLASSA
#endif
#else
#define MAPPOSWND_CLASS      "MapPosWnd"
#endif
#define MAPPOSWND_TAG                _T("MAPPOSWND_TAG")
#define MAPPOSWND_FIRST                (0U-5850U)
#define MAPPOSWND_LAST                (0U-2810U)
#define MAPPOSWND_POSCHANGED        (MAPPOSWND_FIRST - 1)
typedef struct tagMAPPOSWND_NMHDR
{
    NMHDR        nmhdr;
    DWORD        dwData;
    DWORD        dwData1;
    DWORD        dwData2;
} MAPPOSWND_NMHDR;
#endif
class CMapPosWnd
{
public:
    CMapPosWnd();
    ~CMapPosWnd();
public:
    static BOOL RegisterWndClass();
    static CMapPosWnd * FromHandle(HWND hWnd);
    void SetFrameColor(COLORREF color);
    void SetBkgndColor(COLORREF color);
    void SetWndMaxSize(int nMaxSize);
    void SetStageSize(int cx, int cy, BOOL bInvalidata);
    void SetViewPort(LPRECT lpRect, BOOL bInvalidata);
    void GetViewPortRect(LPRECT lprcClient, LPRECT lprcDest);
private:
    void Init();
    void Notify(int nCode, int dwData, int dwData1 = 0, int dwData2 = 0);
    void Release();
    int HighTest(POINT pt);
    static CMapPosWnd* Hook(HWND hWnd);
    static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    static LRESULT OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
    LRESULT OnDestroy(WPARAM wParam, LPARAM lParam);
    LRESULT OnTimer(WPARAM wParam, LPARAM lParam);
    LRESULT OnNcPaint(WPARAM wParam, LPARAM lParam);
    LRESULT OnPaint(WPARAM wParam, LPARAM lParam);
    LRESULT OnMouseMove(WPARAM wParam, LPARAM lParam);
    LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam);
    LRESULT OnLButtonDblclk(WPARAM wParam, LPARAM lParam);
    LRESULT OnMouseWheel(WPARAM wParam, LPARAM lParam);
    LRESULT OnKeyDown(WPARAM wParam, LPARAM lParam);
    LRESULT OnSize(WPARAM wParam, LPARAM lParam);
private:
    HWND        m_hWnd;
    COLORREF    m_crBkgnd;
    COLORREF    m_crFrame;
    COLORREF    m_crViewPort;
private:
    int            m_nWndMaxSize;
    int            m_nStageCx;
    int            m_nStageCy;
    RECT        m_rcViewPort;
};
SourceCode/Bond/Servo/Servo.cpp
@@ -9,6 +9,9 @@
#include "AlarmManager.h"
#include "SECSRuntimeManager.h"
#include "VerticalLine.h"
#include "EqsGraphWnd.h"
#include "MapPosWnd.h"
#include "HmTab.h"
// å£°æ˜Žå…¨å±€å˜é‡ï¼Œç”¨äºŽç®¡ç† GDI+ åˆå§‹åŒ–
@@ -95,6 +98,9 @@
    // æ³¨å†ŒæŽ§ä»¶
    CServoGraph::RegisterWndClass();
    CVerticalLine::RegisterWndClass();
    CEqsGraphWnd::RegisterWndClass();
    CMapPosWnd::RegisterWndClass();
    CHmTab::RegisterWndClass();
    // åˆå§‹åŒ–Rx库
SourceCode/Bond/Servo/Servo.rc
Binary files differ
SourceCode/Bond/Servo/Servo.vcxproj
@@ -219,6 +219,10 @@
    <ClInclude Include="CFliper.h" />
    <ClInclude Include="CLoadPort.h" />
    <ClInclude Include="CMeasurement.h" />
    <ClInclude Include="ColorTransfer.h" />
    <ClInclude Include="CPageGraph1.h" />
    <ClInclude Include="CPageGraph2.h" />
    <ClInclude Include="CPanel.h" />
    <ClInclude Include="CPanelAttributes.h" />
    <ClInclude Include="CPanelEquipment.h" />
    <ClInclude Include="CPanelMaster.h" />
@@ -235,6 +239,8 @@
    <ClInclude Include="Common.h" />
    <ClInclude Include="Configuration.h" />
    <ClInclude Include="Context.h" />
    <ClInclude Include="EqsGraphWnd.h" />
    <ClInclude Include="HmTab.h" />
    <ClInclude Include="HsmsAction.h" />
    <ClInclude Include="HsmsPassive.h" />
    <ClInclude Include="Intent.h" />
@@ -242,6 +248,7 @@
    <ClInclude Include="Log.h" />
    <ClInclude Include="LogDlg.h" />
    <ClInclude Include="LogEdit.h" />
    <ClInclude Include="MapPosWnd.h" />
    <ClInclude Include="Model.h" />
    <ClInclude Include="Resource.h" />
    <ClInclude Include="SECSRuntimeManager.h" />
@@ -280,6 +287,10 @@
    <ClCompile Include="CFliper.cpp" />
    <ClCompile Include="CLoadPort.cpp" />
    <ClCompile Include="CMeasurement.cpp" />
    <ClCompile Include="ColorTransfer.cpp" />
    <ClCompile Include="CPageGraph1.cpp" />
    <ClCompile Include="CPageGraph2.cpp" />
    <ClCompile Include="CPanel.cpp" />
    <ClCompile Include="CPanelAttributes.cpp" />
    <ClCompile Include="CPanelEquipment.cpp" />
    <ClCompile Include="CPanelMaster.cpp" />
@@ -295,6 +306,8 @@
    <ClCompile Include="CMaster.cpp" />
    <ClCompile Include="Configuration.cpp" />
    <ClCompile Include="Context.cpp" />
    <ClCompile Include="EqsGraphWnd.cpp" />
    <ClCompile Include="HmTab.cpp" />
    <ClCompile Include="HsmsAction.cpp" />
    <ClCompile Include="HsmsPassive.cpp" />
    <ClCompile Include="Intent.cpp" />
@@ -302,6 +315,7 @@
    <ClCompile Include="Log.cpp" />
    <ClCompile Include="LogDlg.cpp" />
    <ClCompile Include="LogEdit.cpp" />
    <ClCompile Include="MapPosWnd.cpp" />
    <ClCompile Include="Model.cpp" />
    <ClCompile Include="SECSRuntimeManager.cpp" />
    <ClCompile Include="SecsTestDlg.cpp" />
SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -67,6 +67,13 @@
    <ClCompile Include="CBakeCooling.cpp" />
    <ClCompile Include="CVacuumBake.cpp" />
    <ClCompile Include="Intent.cpp" />
    <ClCompile Include="CPanel.cpp" />
    <ClCompile Include="EqsGraphWnd.cpp" />
    <ClCompile Include="ColorTransfer.cpp" />
    <ClCompile Include="MapPosWnd.cpp" />
    <ClCompile Include="HmTab.cpp" />
    <ClCompile Include="CPageGraph1.cpp" />
    <ClCompile Include="CPageGraph2.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="AlarmManager.h" />
@@ -132,6 +139,13 @@
    <ClInclude Include="CBakeCooling.h" />
    <ClInclude Include="CVacuumBake.h" />
    <ClInclude Include="Intent.h" />
    <ClInclude Include="CPanel.h" />
    <ClInclude Include="EqsGraphWnd.h" />
    <ClInclude Include="ColorTransfer.h" />
    <ClInclude Include="MapPosWnd.h" />
    <ClInclude Include="HmTab.h" />
    <ClInclude Include="CPageGraph1.h" />
    <ClInclude Include="CPageGraph2.h" />
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="Servo.rc" />
SourceCode/Bond/Servo/ServoDlg.cpp
@@ -13,28 +13,12 @@
#include <chrono>
#include <thread>
#include <cmath>
#include "HmTab.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// Image
#define IMAGE_ROBOT                2
#define INDICATE_BONDER1        1
#define INDICATE_BONDER2        2
#define INDICATE_FLIPER            3
#define INDICATE_ALIGNER        4
#define INDICATE_LPORT4            5
#define INDICATE_LPORT3            6
#define INDICATE_LPORT2            7
#define INDICATE_LPORT1            8
#define INDICATE_ROBOT_ARM1        9
#define INDICATE_ROBOT_ARM2        10
#define INDICATE_VACUUM_BAKE    11
#define INDICATE_BAKE_COOLING    12
#define INDICATE_MEASUREMENT    13
/* åˆ›å»ºç»ˆç«¯çš„定时器 */
@@ -86,7 +70,6 @@
    m_hbrBkgnd = nullptr;
    m_bShowLogWnd = FALSE;
    m_bShowAlarmWnd = FALSE;
    m_bIsRobotMoving = FALSE;
    m_pLogDlg = nullptr;
    m_pAlarmDlg = nullptr;
    m_pTerminalDisplayDlg = nullptr;
@@ -94,6 +77,8 @@
    m_pPanelMaster = nullptr;
    m_pPanelEquipment = nullptr;
    m_pPanelAttributes = nullptr;
    m_pPageGraph1 = nullptr;
    m_pPageGraph2 = nullptr;
}
void CServoDlg::DoDataExchange(CDataExchange* pDX)
@@ -135,11 +120,10 @@
    ON_COMMAND(ID_MENU_HELP_ABOUT, &CServoDlg::OnMenuHelpAbout)
    ON_WM_INITMENUPOPUP()
    ON_WM_TIMER()
    ON_WM_ERASEBKGND()
    ON_BN_CLICKED(IDC_BUTTON_ALARM, &CServoDlg::OnBnClickedButtonAlarm)
    ON_BN_CLICKED(IDC_BUTTON_ALARM, &CServoDlg::OnBnClickedButtonAlarm)
    ON_NOTIFY(BYSERVOGRAPH_ITEM_CLICKED, IDC_SERVO_GRAPH1, &CServoDlg::OnGraphItemClicked)
    ON_MESSAGE(ID_MSG_PANEL_RESIZE, OnPanelResize)
    ON_NOTIFY(BYHMTAB_SEL_CHANGED, IDC_TAB1, &CServoDlg::OnTabSelChanged)
END_MESSAGE_MAP()
@@ -160,21 +144,6 @@
                const char* pszText;
                if (pAny->getStringValue("text", pszText)) {
                    ShowTerminalText(pszText);
                }
            }
            else if (RX_CODE_EQ_ALIVE == code) {
                // é€šçŸ¥è®¾å¤‡çŠ¶æ€
                SERVO::CEquipment* pEquipment = nullptr;
                if (pAny->getPtrValue("ptr", (void*&)pEquipment)) {
                    if (pEquipment != nullptr) {
                        int nID = pEquipment->getID();
                        BOOL bAlive = pEquipment->isAlive();
                        if (EQ_ID_EFEM == nID) {
                            DeviceStatus status = bAlive ? DeviceStatus::ONLINE : DeviceStatus::OFFLINE;
                            UpdateDeviceStatus(INDICATE_ROBOT_ARM1, status);
                            UpdateDeviceStatus(INDICATE_ROBOT_ARM2, status);
                        }
                    }
                }
            }
            else if (RX_CODE_SELECT_EQUIPMENT == code) {
@@ -256,89 +225,20 @@
    // Í¼Ê¾
    m_pGraph = CServoGraph::Hook(GetDlgItem(IDC_SERVO_GRAPH1)->GetSafeHwnd());
    CString strPath;
    strPath.Format(_T("%s\\res\\Servo001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
    m_pGraph->AddImage(1, (LPTSTR)(LPCTSTR)strPath, 0, 0);
    // Tab
    m_pPageGraph1 = new CPageGraph1();
    m_pPageGraph1->Create(IDD_PAGE_GRAPH1, this);
    m_pPageGraph2 = new CPageGraph2();
    m_pPageGraph2->Create(IDD_PAGE_GRAPH2, this);
    strPath.Format(_T("%s\\res\\Robot001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
    m_pGraph->AddImage(IMAGE_ROBOT, (LPTSTR)(LPCTSTR)strPath, 170, 270);
    // æ·»åŠ æŒ‡ç¤ºå™¨
    // Bonder
    m_pGraph->AddIndicateBox(INDICATE_BONDER1, 220, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_BONDER1, "10", "Bonder 1");
    m_pGraph->AddIndicateBox(INDICATE_BONDER2, 220, 516, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_BONDER2, "11", "Bonder 2");
    // ç¿»è½¬
    m_pGraph->AddIndicateBox(INDICATE_FLIPER, 338, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_FLIPER, "8", "Fliper");
    // å¯¹ä½
    m_pGraph->AddIndicateBox(INDICATE_ALIGNER, 428, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_ALIGNER, "7", "Aligner");
    // Load port 4
    m_pGraph->AddIndicateBox(INDICATE_LPORT4, 518, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT4, "4", "LPort4");
    // Load port 3
    m_pGraph->AddIndicateBox(INDICATE_LPORT3, 606, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT3, "3", "LPort3");
    // Load port 2
    m_pGraph->AddIndicateBox(INDICATE_LPORT2, 690, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT2, "2", "LPort2");
    // Load port 1
    m_pGraph->AddIndicateBox(INDICATE_LPORT1, 774, 172, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT1, "1", "LPort1");
    // Robot
    m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM1, 190, 294, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_ROBOT_ARM1, "5", "Robot");
    m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM2, 243, 294, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_ROBOT_ARM2, "6", "Robot");
    // Vacuum bake
    m_pGraph->AddIndicateBox(INDICATE_VACUUM_BAKE, 396, 516, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_VACUUM_BAKE, "9", "Vacuum bake");
    // Bake cooling
    m_pGraph->AddIndicateBox(INDICATE_BAKE_COOLING, 566, 516, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_BAKE_COOLING, "12", "Bake cooling");
    // ç²¾åº¦æ£€
    m_pGraph->AddIndicateBox(INDICATE_MEASUREMENT, 737, 516, 48, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_MEASUREMENT, "13", "Measurement");
    CHmTab* m_pTab = CHmTab::Hook(GetDlgItem(IDC_TAB1)->m_hWnd);
    m_pTab->SetPaddingLeft(20);
    m_pTab->SetItemMarginLeft(18);
    m_pTab->AddItem("报表", FALSE);
    m_pTab->AddItem("日志", TRUE);
    m_pTab->SetCurSel(0);
    m_pTab->SetBkgndColor(RGB(222, 222, 222));
    ShowChildPage(0);
    m_pPanelMaster = new CPanelMaster();
@@ -373,13 +273,6 @@
    // ç›¸å½“于延时调用master的初始化
    theApp.m_model.m_master.init();
    // ç»‘定数据
    {
        SERVO::CEquipment* pEquipment = theApp.m_model.m_master.getEquipment(EQ_ID_EFEM);
        m_pGraph->SetIndicateBoxData(INDICATE_ROBOT_ARM1, pEquipment);
    }
    return TRUE;  // é™¤éžå°†ç„¦ç‚¹è®¾ç½®åˆ°æŽ§ä»¶ï¼Œå¦åˆ™è¿”回 TRUE
@@ -572,11 +465,17 @@
void CServoDlg::OnMenuTestMessageClear()
{
    /*
    SERVO::CEquipment* pEquipment = m_pPanelMaster->GetActiveEquipment();
    if (pEquipment != nullptr) {
        SERVO::CEqCimMessageClearStep* pStep = (SERVO::CEqCimMessageClearStep*)pEquipment->getStepWithName(STEP_CIM_MESSAGE_CLEAR);
        pStep->clearCimMessage(2, 3);
    }
    */
    SERVO::CLoadPort* pLoadPort1 =
        (SERVO::CLoadPort*)theApp.m_model.m_master.getEquipment(EQ_ID_LOADPORT1);
    pLoadPort1->outputPanel();
}
void CServoDlg::OnUpdateMenuTestMessageClear(CCmdUI* pCmdUI)
@@ -657,6 +556,18 @@
        m_pPanelAttributes->DestroyWindow();
        delete m_pPanelAttributes;
        m_pPanelAttributes = nullptr;
    }
    if (m_pPageGraph1 != nullptr) {
        m_pPageGraph1->DestroyWindow();
        delete m_pPageGraph1;
        m_pPageGraph1 = nullptr;
    }
    if (m_pPageGraph2 != nullptr) {
        m_pPageGraph2->DestroyWindow();
        delete m_pPageGraph2;
        m_pPageGraph2 = nullptr;
    }
    
    if (m_hbrBkgnd != nullptr) {
@@ -750,142 +661,13 @@
    m_btnAlarm.Invalidate();
}
void CServoDlg::UpdateRobotPosition(float percentage)
{
    // é™åˆ¶ç™¾åˆ†æ¯”范围在 [0, 1] ä¹‹é—´
    if (percentage < 0.0f) percentage = 0.0f;
    if (percentage > 1.0f) percentage = 1.0f;
    // æ ¹æ®ç™¾åˆ†æ¯”计算目标 X åæ ‡
    int startX = m_pGraph->GetImage(IMAGE_ROBOT)->x;
    int endX = static_cast<int>(170 + percentage * (700 - 170));
    int arm1Offset = 20;  // ä»Žå›¾ç‰‡åˆ°ARM1的偏移
    int arm2Offset = 73;  // ä»Žå›¾ç‰‡åˆ°ARM2的偏移
    // è®¡ç®—移动所需的时间
    int distance = abs(endX - startX);
    int duration = static_cast<int>((distance / 100.0) * 1000);
    auto startTime = std::chrono::steady_clock::now();
    auto endTime = startTime + std::chrono::milliseconds(duration);
    // å¼€å§‹ç§»åŠ¨ï¼Œè®¾ç½®æ ‡è®°
    m_bIsRobotMoving = TRUE;
    // å¼€å§‹å¹³æ»‘移动
    while (std::chrono::steady_clock::now() < endTime) {
        auto currentTime = std::chrono::steady_clock::now();
        float progress = std::chrono::duration<float, std::milli>(currentTime - startTime).count() / duration;
        progress = min(progress, 1.0f);
        // æ ¹æ®è¿›åº¦è®¡ç®—当前位置
        int currentX = static_cast<int>(startX + progress * (endX - startX));
        m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, currentX, 270);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, currentX + arm1Offset, 294);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, currentX + arm2Offset, 294);
        // åˆ·æ–°ç•Œé¢
        Invalidate();
        UpdateWindow();
        // æŽ§åˆ¶å¸§çŽ‡çº¦ä¸º 60 FPS
        std::this_thread::sleep_for(std::chrono::milliseconds(16));
    }
    // ç¡®ä¿æœ€åŽä½ç½®ç²¾ç¡®åˆ°ç›®æ ‡ä½ç½®
    m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, endX, 270);
    m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, endX + arm1Offset, 294);
    m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, endX + arm2Offset, 294);
    // ç•Œé¢é‡ç»˜
    Invalidate();
    // åŠ¨ç”»ç»“æŸï¼Œè®¾ç½®æ ‡è®°
    m_bIsRobotMoving = FALSE;
}
void CServoDlg::RotateRobot(float angleInDegrees)
{
    // å°†è§’度转换为弧度
    float angleInRadians = static_cast<float>(std::acos(-1)) / 180.0f * angleInDegrees;
    // èŽ·å–æœºå™¨äººå›¾ç‰‡çš„å½“å‰åæ ‡å’Œä¸­å¿ƒ
    auto* pImage = m_pGraph->GetImage(IMAGE_ROBOT);
    if (!pImage) return;
    // æ›´æ–° Rotate å›¾ç‰‡çš„角度,确保角度保持在 [0, 360) èŒƒå›´å†…
    m_pGraph->UpdateImageAngle(IMAGE_ROBOT, static_cast<float>(fmod(pImage->angle + angleInDegrees + 360, 360)));
    int cx = pImage->x + pImage->bmWidth / 2;  // å›¾ç‰‡ä¸­å¿ƒ X
    int cy = pImage->y + pImage->bmHeight / 2; // å›¾ç‰‡ä¸­å¿ƒ Y
    // æ—‹è½¬æŒ‡ç¤ºæ¡†çš„坐标
    auto* pRobot1 = m_pGraph->GetIndicateBox(INDICATE_ROBOT_ARM1);
    auto* pRobot2 = m_pGraph->GetIndicateBox(INDICATE_ROBOT_ARM2);
    if (pRobot1 && pRobot2) {
        int newArmX1 = pImage->x + 20;
        int newArmY1 = 294;
        int newArmX2 = pImage->x + 73;
        int newArmY2 = 294;
        if (angleInDegrees != 0.0f) {
            // è®¡ç®—指示框1的新坐标
            newArmX1 = static_cast<int>(cx + (pRobot1->x - cx) * cos(angleInRadians) - (pRobot1->y - cy) * sin(angleInRadians));
            newArmY1 = static_cast<int>(cy + (pRobot1->x - cx) * sin(angleInRadians) + (pRobot1->y - cy) * cos(angleInRadians));
            // è®¡ç®—指示框2的新坐标
            newArmX2 = static_cast<int>(cx + (pRobot2->x - cx) * cos(angleInRadians) - (pRobot2->y - cy) * sin(angleInRadians));
            newArmY2 = static_cast<int>(cy + (pRobot2->x - cx) * sin(angleInRadians) + (pRobot2->y - cy) * cos(angleInRadians));
        }
        // æ›´æ–°æŒ‡ç¤ºæ¡†çš„位置
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, newArmX1, newArmY1);
        m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, newArmX2, newArmY2);
    }
    // å¼ºåˆ¶é‡ç»˜ç•Œé¢
    Invalidate();
}
void CServoDlg::UpdateDeviceStatus(int id, DeviceStatus status)
{
    // æ ¹æ®çŠ¶æ€è®¾ç½®é¢œè‰²
    COLORREF newBackgroundColor;
    COLORREF newFrameColor1;
    COLORREF newFrameColor2;
    switch (status) {
    case ONLINE:
        newBackgroundColor = EQ_BOX_ONLINE;
        newFrameColor1 = EQ_BOX_FRAME1;
        newFrameColor2 = EQ_BOX_FRAME2;
        break;
    case OFFLINE:
        newBackgroundColor = RGB(222, 222, 222);
        newFrameColor1 = EQ_BOX_FRAME1;
        newFrameColor2 = EQ_BOX_FRAME2;
        break;
    default:
        newBackgroundColor = RGB(255, 255, 255); // é»˜è®¤ç™½è‰²èƒŒæ™¯
        newFrameColor1 = RGB(0, 0, 0);           // é»˜è®¤é»‘色框架1
        newFrameColor2 = RGB(0, 0, 0);           // é»˜è®¤é»‘色框架2
        break;
    }
    m_pGraph->UpdateIndicateBox1Colors(id, newBackgroundColor, newFrameColor1, newFrameColor2);
    // åˆ·æ–°ç•Œé¢
    Invalidate();
    UpdateWindow();
}
void CServoDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);
    if (GetDlgItem(IDC_SERVO_GRAPH1) == nullptr) return;
    if (GetDlgItem(IDC_TAB1) == nullptr) return;
    if (m_pPageGraph1 == nullptr) return;
    if (m_pPageGraph2 == nullptr) return;
    Resize();
    Invalidate();
}
@@ -918,11 +700,16 @@
        x += nPanelWidth;
    }
    pItem = GetDlgItem(IDC_SERVO_GRAPH1);
    pItem->GetClientRect(&rcItem);
    pItem->MoveWindow(x, y, rcItem.Width(), rcItem.Height());
    pItem = GetDlgItem(IDC_TAB1);
    pItem->GetWindowRect(rcItem);
    pItem->MoveWindow(x, y, rcClient.Width() - x, rcItem.Height());
    y += rcItem.Height();
    y += 8;
    m_pPageGraph1->MoveWindow(x, y, rcClient.Width() - x, rcClient.Height());
    m_pPageGraph2->MoveWindow(x, y, rcClient.Width() - x, rcClient.Height());
    x = rcClient.right - 8;
    pItem = GetDlgItem(IDC_BUTTON_LOG);
@@ -1013,31 +800,6 @@
    CDialogEx::OnTimer(nIDEvent);
}
BOOL CServoDlg::OnEraseBkgnd(CDC* pDC)
{
    // TODO: åœ¨æ­¤æ·»åŠ æ¶ˆæ¯å¤„ç†ç¨‹åºä»£ç å’Œ/或调用默认值
    if (m_bIsRobotMoving) {
        // ç¦æ­¢åˆ·æ–°èƒŒæ™¯ï¼Œé¿å…é—ªçƒ
        return TRUE;
    }
    return CDialogEx::OnEraseBkgnd(pDC);
}
void CServoDlg::OnGraphItemClicked(NMHDR* pNMHDR, LRESULT* pResult)
{
    BYSERVOGRAPH_NMHDR* pGraphNmhdr = reinterpret_cast<BYSERVOGRAPH_NMHDR*>(pNMHDR);
    CString s; s.Format(_T("OnGraphItemClicked %d"), pGraphNmhdr->dwData);
    SERVO::CEquipment* pEquipment = (SERVO::CEquipment*)m_pGraph->GetIndicateBoxData(pGraphNmhdr->dwData);
    if (pEquipment != nullptr) {
        AfxMessageBox(pEquipment->getName().c_str());
    }
    *pResult = 0;
}
LRESULT CServoDlg::OnPanelResize(WPARAM wParam, LPARAM lParam)
{
    int width = wParam;
@@ -1047,3 +809,21 @@
    return 0;
}
void CServoDlg::OnTabSelChanged(NMHDR* nmhdr, LRESULT* result)
{
    BYHMTAB_NMHDR* pNmhdrex = (BYHMTAB_NMHDR*)nmhdr;
    ShowChildPage((int)pNmhdrex->dwData);
    *result = 0;
}
void CServoDlg::ShowChildPage(int index)
{
    ASSERT(0 <= index && index < 3);
    static CWnd* pPages[] = { m_pPageGraph1, m_pPageGraph2 };
    for (int i = 0; i < 2; i++) {
        pPages[i]->ShowWindow(i == index ? SW_SHOW : SW_HIDE);
    }
}
SourceCode/Bond/Servo/ServoDlg.h
@@ -3,7 +3,6 @@
//
#pragma once
#include "ServoGraph.h"
#include "BlButton.h"
#include "LogDlg.h"
#include "AlarmDlg.h"
@@ -11,12 +10,9 @@
#include "CPanelMaster.h"
#include "CPanelEquipment.h"
#include "CPanelAttributes.h"
#include "CPageGraph1.h"
#include "CPageGraph2.h"
enum DeviceStatus {
    ONLINE,       // åœ¨çº¿
    OFFLINE,      // ç¦»çº¿
};
// CServoDlg å¯¹è¯æ¡†
class CServoDlg : public CDialogEx
@@ -34,9 +30,7 @@
    void Resize();
    void UpdateLogBtn();
    void UpdateAlarmBtn();
    void UpdateRobotPosition(float percentage);
    void RotateRobot(float angleInDegrees);
    void UpdateDeviceStatus(int id, DeviceStatus status);
    void ShowChildPage(int index);
private:
@@ -46,6 +40,8 @@
    CLogDlg* m_pLogDlg;
    CAlarmDlg* m_pAlarmDlg;
    CTerminalDisplayDlg* m_pTerminalDisplayDlg;
    CPageGraph1* m_pPageGraph1;
    CPageGraph2* m_pPageGraph2;
// å¯¹è¯æ¡†æ•°æ®
@@ -57,13 +53,9 @@
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
private:
    BOOL m_bIsRobotMoving;
// å®žçް
protected:
    HICON m_hIcon;
    CServoGraph* m_pGraph;
    COLORREF m_crBkgnd;
    HBRUSH m_hbrBkgnd;
    CBlButton m_btnLog;
@@ -107,8 +99,7 @@
    afx_msg void OnUpdateMenuTestMessageClear(CCmdUI* pCmdUI);
    afx_msg void OnMenuHelpAbout();
    afx_msg void OnTimer(UINT_PTR nIDEvent);
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    afx_msg void OnBnClickedButtonAlarm();
    afx_msg void OnGraphItemClicked(NMHDR* pNMHDR, LRESULT* pResult);
    afx_msg LRESULT OnPanelResize(WPARAM wParam, LPARAM lParam);
    afx_msg void OnTabSelChanged(NMHDR* nmhdr, LRESULT* result);
};
SourceCode/Bond/Servo/resource.h
Binary files differ