1.状态指示图,目前灰色表示掉线,绿色表示在线。增加Slot的小点表示有没有料,及加工状态 。
2.增加图示
已修改7个文件
327 ■■■■■ 文件已修改
SourceCode/Bond/Servo/CEquipment.cpp 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipment.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageGraph1.cpp 230 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageGraph1.h 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Common.h 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoGraph.cpp 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoGraph.h 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipment.cpp
@@ -2299,6 +2299,14 @@
        }
    }
    PROCESS_STATE CEquipment::getProcessState(int slotNo) const
    {
        if (slotNo <= 0 || slotNo > SLOT_MAX) {
            return PROCESS_STATE::Ready;
        }
        return m_processState[slotNo - 1];
    }
    std::vector<SERVO::CSVData>& CEquipment::getSVDatas()
    {
        return m_svDatas;
SourceCode/Bond/Servo/CEquipment.h
@@ -237,6 +237,8 @@
        // 字符串检测结果转换
        InspResult judgeStringToInspResult(std::string& strJudge);
        PROCESS_STATE getProcessState(int slotNo) const;
        // for test
        void fireSetProcessState(int nSlotNo, PROCESS_STATE state) { return setProcessState(nSlotNo, state); }
SourceCode/Bond/Servo/CPageGraph1.cpp
@@ -6,6 +6,8 @@
#include "CPageGraph1.h"
#include "afxdialogex.h"
#include "Common.h"
#include "CEquipment.h"
#include "CGlass.h"
const std::map<SERVO::ROBOT_POSITION, RobotPositionMapping> g_positionMap = {
    { SERVO::ROBOT_POSITION::Port1,     { SERVO::ROBOT_POSITION::Port1,     1.00f,   0.00f } },
@@ -23,6 +25,7 @@
// Image
#define IMAGE_ROBOT                2
#define IMAGE_LEGEND            3
#define INDICATE_BONDER1        1
#define INDICATE_BONDER2        2
@@ -55,6 +58,7 @@
    m_pObserver = nullptr;                              // 观察者对象(可能是事件观察者)
    m_crBkgnd = PAGE_GRPAH1_BACKGROUND_COLOR;           // 背景颜色
    m_hbrBkgnd = nullptr;                               // 背景刷句柄
    m_slotBarTestMode = 0;                           // 0=off,1=has,2=processing
    // ===== 机器人动画状态初始化 =====
    m_bIsRobotMoving = FALSE;                           // 当前是否正在动画移动
@@ -108,6 +112,28 @@
    std::string configDir = exePath.substr(0, exePath.find_last_of("\\/")) + "\\Config";
    CreateDirectoryA(configDir.c_str(), NULL);
    return configDir + "\\robot_offset.ini";
}
void CPageGraph1::UpdateLegendPosition()
{
    if (!m_pGraph) return;
    auto* pImage = m_pGraph->GetImage(IMAGE_LEGEND);
    if (!pImage) return;
    RECT rc = { 0 };
    ::GetClientRect(m_pGraph->GetSafeWnd(), &rc);
    std::string iniPath = GetConfigPath();
    int cfgX = GetPrivateProfileIntA("Graph1", "LegendX", -1, iniPath.c_str());
    int cfgY = GetPrivateProfileIntA("Graph1", "LegendY", -1, iniPath.c_str());
    int x = (cfgX >= 0) ? cfgX : (rc.right - pImage->bmWidth - 8);
    int y = (cfgY >= 0) ? cfgY : 6;
    if (x < 0) x = 0;
    if (y < 0) y = 0;
    if (x > rc.right - pImage->bmWidth) x = rc.right - pImage->bmWidth;
    if (y > rc.bottom - pImage->bmHeight) y = rc.bottom - pImage->bmHeight;
    m_pGraph->UpdateImageCoordinates(IMAGE_LEGEND, x, y);
    m_pGraph->Invalidata();
}
void CPageGraph1::InitRxWindows()
@@ -192,78 +218,138 @@
    strPath.Format(_T("%s\\res\\Robot001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
    m_pGraph->AddImage(IMAGE_ROBOT, (LPTSTR)(LPCTSTR)strPath, 170, 270);
    // Legend
    strPath.Format(_T("%s\\res\\GraphLegend.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
    m_pGraph->AddImage(IMAGE_LEGEND, (LPTSTR)(LPCTSTR)strPath, 0, 0);
    UpdateLegendPosition();
    // 添加指示器
    // Bonder
    m_pGraph->AddIndicateBox(INDICATE_BONDER1, 220, 172, 48, RGB(22, 22, 22),
    // size config
    std::string iniPath = GetConfigPath();
    int boxSize = GetPrivateProfileIntA("Graph1", "BoxSize", 56, iniPath.c_str());
    if (boxSize < 40) boxSize = 40;
    if (boxSize > 80) boxSize = 80;
    int slotSize = GetPrivateProfileIntA("Graph1", "SlotSize", 6, iniPath.c_str());
    if (slotSize < 2) slotSize = 2;
    if (slotSize > 12) slotSize = 12;
    m_pGraph->SetSlotBarSize(slotSize);
    int armBoxSize = GetPrivateProfileIntA("Graph1", "ArmBoxSize", boxSize, iniPath.c_str());
    if (armBoxSize < 30) armBoxSize = 30;
    if (armBoxSize > 80) armBoxSize = 80;
    // ArmSpacing = edge-to-edge gap between the two robot arm boxes.
    int armGap = GetPrivateProfileIntA("Graph1", "ArmSpacing", 6, iniPath.c_str());
    if (armGap < 0) armGap = 0;
    if (armGap > 100) armGap = 100;
    int arm1X = 190;
    int arm2X = 243;
    int armY = 294;
    int minArmSpacing = armBoxSize + armGap;
    if (minArmSpacing > 0) {
        int mid = (m_arm1Offset.x + m_arm2Offset.x) / 2;
        int half = minArmSpacing / 2;
        if (m_arm1Offset.x <= m_arm2Offset.x) {
            m_arm1Offset.x = mid - half;
            m_arm2Offset.x = mid + half;
        } else {
            m_arm2Offset.x = mid - half;
            m_arm1Offset.x = mid + half;
        }
    }
    {
        int baseMid = (arm1X + arm2X) / 2;
        int half = minArmSpacing / 2;
        arm1X = baseMid - half;
        arm2X = baseMid + half;
    }
    m_pGraph->AddIndicateBox(INDICATE_BONDER1, 220, 172, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_BONDER1, "", "Bonder 1");
    m_pGraph->AddIndicateBox(INDICATE_BONDER2, 220, 516, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_BONDER2, 220, 516, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_BONDER2, "", "Bonder 2");
    // 翻转
    m_pGraph->AddIndicateBox(INDICATE_FLIPER, 338, 172, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_FLIPER, 338, 172, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_FLIPER, "", "Fliper");
    // 对位
    m_pGraph->AddIndicateBox(INDICATE_ALIGNER, 428, 172, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_ALIGNER, 428, 172, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_ALIGNER, "", "Aligner");
    // Load port 4
    m_pGraph->AddIndicateBox(INDICATE_LPORT4, 518, 172, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_LPORT4, 518, 172, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT4, "", "LPort4");
    // Load port 3
    m_pGraph->AddIndicateBox(INDICATE_LPORT3, 606, 172, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_LPORT3, 606, 172, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT3, "", "LPort3");
    // Load port 2
    m_pGraph->AddIndicateBox(INDICATE_LPORT2, 690, 172, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_LPORT2, 690, 172, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT2, "", "LPort2");
    // Load port 1
    m_pGraph->AddIndicateBox(INDICATE_LPORT1, 774, 172, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_LPORT1, 774, 172, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_LPORT1, "", "LPort1");
    // Robot
    m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM1, 190, 294, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM1, arm1X, armY, armBoxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_ROBOT_ARM1, "", "Robot");
    m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM2, 243, 294, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM2, arm2X, armY, armBoxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_ROBOT_ARM2, "", "Robot");
    // Vacuum bake
    m_pGraph->AddIndicateBox(INDICATE_VACUUM_BAKE, 396, 516, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_VACUUM_BAKE, 396, 516, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_VACUUM_BAKE, "", "Vacuum bake");
    // Bake cooling
    m_pGraph->AddIndicateBox(INDICATE_BAKE_COOLING, 566, 516, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_BAKE_COOLING, 566, 516, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_BAKE_COOLING, "", "Bake cooling");
    // 精度检
    m_pGraph->AddIndicateBox(INDICATE_MEASUREMENT, 737, 516, 48, RGB(22, 22, 22),
    m_pGraph->AddIndicateBox(INDICATE_MEASUREMENT, 737, 516, boxSize, RGB(22, 22, 22),
        RGB(255, 127, 39), EQ_BOX_OFFLINE);
    m_pGraph->SetBoxText(INDICATE_MEASUREMENT, "", "Measurement");
    // slot bar positions (top row / bottom row)
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_BONDER1, SlotBarPos::Top);
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_FLIPER, SlotBarPos::Top);
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_ALIGNER, SlotBarPos::Top);
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_LPORT4, SlotBarPos::Top);
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_LPORT3, SlotBarPos::Top);
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_LPORT2, SlotBarPos::Top);
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_LPORT1, SlotBarPos::Top);
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_BONDER2, SlotBarPos::Bottom);
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_VACUUM_BAKE, SlotBarPos::Bottom);
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_BAKE_COOLING, SlotBarPos::Bottom);
    m_pGraph->SetIndicateBoxSlotBarPosition(INDICATE_MEASUREMENT, SlotBarPos::Bottom);
    UpdateSlotBars();
    return TRUE;  // return TRUE unless you set the focus to a control
                  // 异常: OCX 属性页应返回 FALSE
@@ -360,6 +446,7 @@
    CRect rcClient;
    GetClientRect(&rcClient);
    GetDlgItem(IDC_SERVO_GRAPH1)->MoveWindow(0, 0, rcClient.Width(), rcClient.Height());
    UpdateLegendPosition();
}
void CPageGraph1::UpdateRobotPosition(float percentage)
@@ -542,6 +629,111 @@
    return pt;
}
void CPageGraph1::UpdateSlotBars()
{
    if (m_pGraph == nullptr) return;
    if (m_slotBarTestMode != 0) {
        ApplySlotBarTestPattern(m_slotBarTestMode);
        return;
    }
    struct SlotBarBind {
        int eqId;
        int indicateId;
    };
    static const SlotBarBind kSlotBars[] = {
        { EQ_ID_Bonder1, INDICATE_BONDER1 },
        { EQ_ID_FLIPER, INDICATE_FLIPER },
        { EQ_ID_ALIGNER, INDICATE_ALIGNER },
        { EQ_ID_LOADPORT4, INDICATE_LPORT4 },
        { EQ_ID_LOADPORT3, INDICATE_LPORT3 },
        { EQ_ID_LOADPORT2, INDICATE_LPORT2 },
        { EQ_ID_LOADPORT1, INDICATE_LPORT1 },
        { EQ_ID_Bonder2, INDICATE_BONDER2 },
        { EQ_ID_VACUUMBAKE, INDICATE_VACUUM_BAKE },
        { EQ_ID_BAKE_COOLING, INDICATE_BAKE_COOLING },
        { EQ_ID_MEASUREMENT, INDICATE_MEASUREMENT },
    };
    for (const auto& item : kSlotBars) {
        SERVO::CEquipment* pEq = theApp.m_model.m_master.getEquipment(item.eqId);
        std::vector<COLORREF> colors;
        BuildSlotColors(pEq, colors);
        m_pGraph->SetIndicateBoxSlotColors(item.indicateId, colors);
    }
}
void CPageGraph1::BuildSlotColors(SERVO::CEquipment* pEq, std::vector<COLORREF>& colors)
{
    colors.clear();
    if (pEq == nullptr) return;
    for (int i = 0; i < SLOT_MAX; ++i) {
        SERVO::CSlot* pSlot = pEq->getSlot(i);
        if (pSlot == nullptr || !pSlot->isEnable()) continue;
        SERVO::CGlass* pGlass = (SERVO::CGlass*)pSlot->getContext();
        BOOL isProcessing = FALSE;
        if (pGlass != nullptr) {
            const auto st = pEq->getProcessState(i + 1);
            isProcessing = (st == SERVO::PROCESS_STATE::Processing);
        }
        colors.push_back(GetSlotColor(pGlass, isProcessing));
    }
}
COLORREF CPageGraph1::GetSlotColor(SERVO::CGlass* pGlass, BOOL isProcessing)
{
    if (pGlass == nullptr) {
        return EQ_SLOT_EMPTY;
    }
    const auto type = pGlass->getType();
    const bool isG2 = (type == SERVO::MaterialsType::G2 || type == SERVO::MaterialsType::G1G2);
    if (isProcessing) {
        return isG2 ? EQ_SLOT_PROC_G2 : EQ_SLOT_PROC_G1;
    }
    return isG2 ? EQ_SLOT_G2 : EQ_SLOT_G1;
}
void CPageGraph1::ApplySlotBarTestPattern(int mode)
{
    if (m_pGraph == nullptr) return;
    std::vector<COLORREF> colors;
    colors.reserve(SLOT_MAX);
    for (int i = 0; i < SLOT_MAX; ++i) {
        if (mode == 2) {
            colors.push_back((i % 2 == 0) ? EQ_SLOT_PROC_G1 : EQ_SLOT_PROC_G2);
        } else if (mode == 1) {
            colors.push_back((i % 2 == 0) ? EQ_SLOT_G1 : EQ_SLOT_G2);
        } else {
            colors.push_back(EQ_SLOT_EMPTY);
        }
    }
    struct SlotBarBind {
        int indicateId;
    };
    static const SlotBarBind kSlotBars[] = {
        { INDICATE_BONDER1 },
        { INDICATE_FLIPER },
        { INDICATE_ALIGNER },
        { INDICATE_LPORT4 },
        { INDICATE_LPORT3 },
        { INDICATE_LPORT2 },
        { INDICATE_LPORT1 },
        { INDICATE_BONDER2 },
        { INDICATE_VACUUM_BAKE },
        { INDICATE_BAKE_COOLING },
        { INDICATE_MEASUREMENT },
    };
    for (const auto& item : kSlotBars) {
        m_pGraph->SetIndicateBoxSlotColors(item.indicateId, colors);
    }
}
void CPageGraph1::SaveArmOffset(const std::string& armName, const POINT& pt)
{
    std::string iniPath = GetConfigPath();
@@ -558,6 +750,13 @@
{
    BYSERVOGRAPH_NMHDR* pGraphNmhdr = reinterpret_cast<BYSERVOGRAPH_NMHDR*>(pNMHDR);
    if (GetKeyState(VK_SHIFT) & 0x8000) {
        // m_slotBarTestMode = (m_slotBarTestMode + 1) % 3;
        UpdateSlotBars();
        *pResult = 0;
        return;
    }
    // 移动到指定位置 (测试使用)
    if (pGraphNmhdr->dwData == INDICATE_LPORT1) {
@@ -568,6 +767,8 @@
void CPageGraph1::OnTimer(UINT_PTR nIDEvent)
{
    UpdateLegendPosition();
    if (TIMER_ID_DEVICE_STATUS == nIDEvent) {
        KillTimer(TIMER_ID_DEVICE_STATUS);
@@ -624,6 +825,7 @@
    else if (nIDEvent == TIMER_ID_ROBOT_STATUS) {
        SERVO::CEFEM* pEFEM = (SERVO::CEFEM*)theApp.m_model.m_master.getEquipment(EQ_ID_EFEM);
        if (!pEFEM || !pEFEM->isAlive()) {
            UpdateSlotBars();
            return;
        }
@@ -647,6 +849,8 @@
        if (robotData.position != m_lastRobotPosition) {
            StartRobotMoveToPosition(robotData.position);
        }
        UpdateSlotBars();
    }
    else if (nIDEvent == TIMER_ID_ROBOT_ANIMATION) {
        if (!m_bIsRobotMoving) {
SourceCode/Bond/Servo/CPageGraph1.h
@@ -2,6 +2,11 @@
#include "ServoGraph.h"
#include "ServoCommo.h"
namespace SERVO {
    class CEquipment;
    class CGlass;
}
enum DeviceStatus {
    ONLINE,       // 在线
    OFFLINE,      // 离线
@@ -38,12 +43,20 @@
    POINT LoadArmOffset(const std::string& armName);
    void SaveArmOffset(const std::string& armName, const POINT& pt);
    void UpdateSlotBars();
    void BuildSlotColors(SERVO::CEquipment* pEq, std::vector<COLORREF>& colors);
    COLORREF GetSlotColor(SERVO::CGlass* pGlass, BOOL isProcessing);
    void ApplySlotBarTestPattern(int mode);
    void UpdateLegendPosition();
private:
    IObserver* m_pObserver;
    CServoGraph* m_pGraph;
    BOOL m_bIsRobotMoving;
    COLORREF m_crBkgnd;
    HBRUSH m_hbrBkgnd;
    int m_slotBarTestMode;
    // ===== 机器人动画相关成员变量 =====
SourceCode/Bond/Servo/Common.h
@@ -44,6 +44,11 @@
#define EQ_BOX_OCCUPIED                     RGB(255, 127, 39)
#define EQ_BOX_FRAME1                        RGB(22, 22, 22)
#define EQ_BOX_FRAME2                        RGB(255, 127, 39)
#define EQ_SLOT_EMPTY                    RGB(200, 200, 200)
#define EQ_SLOT_G1                        RGB(0, 112, 192)
#define EQ_SLOT_G2                        RGB(0, 176, 80)
#define EQ_SLOT_PROC_G1                RGB(255, 127, 39)
#define EQ_SLOT_PROC_G2                RGB(220, 0, 0)
#define CR_MSGBOX_BKGND                        RGB(7, 71, 166)
#define CR_MSGBOX_TITLE                        RGB(200, 216, 246)
#define CR_MSGBOX_MESSAGE                    RGB(200, 216, 246)
SourceCode/Bond/Servo/ServoGraph.cpp
@@ -21,6 +21,7 @@
    m_crBkgnd = RGB(255,255,255);
    m_pHighItem = nullptr;
    m_hWndTooltip = nullptr;
    m_slotBarSize = 0;
}
CServoGraph::~CServoGraph()
@@ -361,6 +362,37 @@
        // text
        ::DrawText(hMemDC, item.szText, static_cast<int>(strlen(item.szText)),
            &rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
        // slot bar
        if (item.slotBarPos != SlotBarPos::None && !item.slotColors.empty()) {
            const int slotCount = static_cast<int>(item.slotColors.size());
            const int gap = 1;
            int slotSize = 4;
            if (slotCount > 0) {
                const int maxSize = (item.box1Width - gap * (slotCount - 1)) / slotCount;
                slotSize = (m_slotBarSize > 0) ? m_slotBarSize : maxSize;
                if (slotSize < 2) slotSize = 2;
            }
            const int barWidth = slotCount * slotSize + gap * (slotCount - 1);
            const int startX = item.x - barWidth / 2;
            const int baseTop = (item.slotBarPos == SlotBarPos::Top)
                ? (item.y - item.box1Width / 2 - slotSize - 2)
                : (item.y + item.box1Width / 2 + 2);
            HBRUSH hbrBorder = CreateSolidBrush(RGB(100, 100, 100));
            for (int i = 0; i < slotCount; ++i) {
                RECT rcSlot;
                rcSlot.left = startX + i * (slotSize + gap);
                rcSlot.top = baseTop;
                rcSlot.right = rcSlot.left + slotSize;
                rcSlot.bottom = rcSlot.top + slotSize;
                HBRUSH hbrSlot = CreateSolidBrush(item.slotColors[i]);
                ::FillRect(hMemDC, &rcSlot, hbrSlot);
                ::FrameRect(hMemDC, &rcSlot, hbrBorder);
                ::DeleteObject(hbrSlot);
            }
            ::DeleteObject(hbrBorder);
        }
    }
@@ -664,6 +696,31 @@
    }
}
void CServoGraph::SetIndicateBoxSlotBarPosition(int id, SlotBarPos pos)
{
    INDICATEBOX* pIndicateBox = GetIndicateBox(id);
    if (pIndicateBox != nullptr) {
        pIndicateBox->slotBarPos = pos;
        InvalidateRect(m_hWnd, nullptr, TRUE);
    }
}
void CServoGraph::SetIndicateBoxSlotColors(int id, const std::vector<COLORREF>& colors)
{
    INDICATEBOX* pIndicateBox = GetIndicateBox(id);
    if (pIndicateBox != nullptr) {
        pIndicateBox->slotColors = colors;
        InvalidateRect(m_hWnd, nullptr, TRUE);
    }
}
void CServoGraph::SetSlotBarSize(int size)
{
    if (size < 0) size = 0;
    m_slotBarSize = size;
    InvalidateRect(m_hWnd, nullptr, TRUE);
}
void CServoGraph::DrawImage(HDC hMemDC, IMAGE& item)
{
    // 载入BMP
SourceCode/Bond/Servo/ServoGraph.h
@@ -62,6 +62,11 @@
#define HMGRAPH_HT_NOWHERE            0x1
#define HMGRAPH_HT_ITEM                0x2
enum class SlotBarPos {
    None = 0,
    Top,
    Bottom
};
class CServoGraph
{
@@ -95,6 +100,7 @@
            this->box2FrameColor = RGB(255, 255, 0);;
            this->bBox2Visible = FALSE;
            this->m_pData = nullptr;
            this->slotBarPos = SlotBarPos::None;
        };
        ~INDICATEBOX() {};
@@ -112,6 +118,8 @@
        BOOL bBox2Visible;
        std::vector<void*> m_contexts;
        void* m_pData;
        SlotBarPos slotBarPos;
        std::vector<COLORREF> slotColors;
    };
    class INDICATEBKGND
@@ -178,6 +186,9 @@
    void UpdateImageAngle(int id, float angle);
    void UpdateIndicateBox1Colors(int id, COLORREF newBackgroundColor, COLORREF newFrameColor1, COLORREF newFrameColor2);
    void UpdateIndicateBox2Colors(int id, COLORREF newBackgroundColor, COLORREF newFrameColor);
    void SetIndicateBoxSlotBarPosition(int id, SlotBarPos pos);
    void SetIndicateBoxSlotColors(int id, const std::vector<COLORREF>& colors);
    void SetSlotBarSize(int size);
private:
    void DrawImage(HDC hMemDC, IMAGE& item);
@@ -193,6 +204,7 @@
    std::vector<INDICATEBKGND> m_indicateBkgnds;
    void* m_pHighItem;
    HWND m_hWndTooltip;
    int m_slotBarSize;
};
#endif // !defined(AFX_EQUIPMENTGRAPH_H__FBB8916A_DE77_4EA3_9C21_E51E6B06194C__INCLUDED_)