LAPTOP-SNT8I5JK\Boounion
2024-12-05 8da35975404dd92801b1006f76f2ea6c200016d2
1.IO测试页面;
已修改11个文件
594 ■■■■ 文件已修改
SourceCode/Bond/BondEq/CPLC.cpp 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/CPLC.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/FileManager/IOManager.cpp 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/FileManager/IOManager.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/FileManager/RecipeManager.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/ToolUnits.cpp 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/ToolUnits.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/View/AxisSettingsDlg.cpp 157 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/View/AxisSettingsDlg.h 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/View/IOMonitoringDlg.cpp 362 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/View/IOMonitoringDlg.h 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/CPLC.cpp
@@ -269,6 +269,12 @@
    return m_pChannel->writeWord(softComponent, addr, value, funOnWrite);
}
int CPLC::writeDWord(MC::SOFT_COMPONENT softComponent, unsigned int addr,
    int value, ONWRITE funOnWrite)
{
    return m_pChannel->writeDWord(softComponent, addr, value, funOnWrite);
}
int CPLC::writeData(MC::SOFT_COMPONENT softComponent, unsigned int addr,
    const char* pszData, unsigned int length, ONWRITE funOnWrite)
{
SourceCode/Bond/BondEq/CPLC.h
@@ -58,6 +58,7 @@
    int readWord(MC::SOFT_COMPONENT softComponent, unsigned int addr, ONREAD funOnRead);
    int readData(MC::SOFT_COMPONENT softComponent, unsigned int addr, unsigned int nReadLen, ONREADDATA funOnReadData);
    int writeWord(MC::SOFT_COMPONENT softComponent, unsigned int addr, int value, ONWRITE funOnWrite);
    int writeDWord(MC::SOFT_COMPONENT softComponent, unsigned int addr, int value, ONWRITE funOnWrite);
    int writeBit(MC::SOFT_COMPONENT softComponent, unsigned int addr, BOOL bValue, ONWRITE funOnWrite);
    int writeData(MC::SOFT_COMPONENT softComponent, unsigned int addr, const char* pszData, unsigned int length, ONWRITE funOnWrite);
SourceCode/Bond/BondEq/FileManager/IOManager.cpp
@@ -15,7 +15,9 @@
    m_machines[machineName] = data;
}
void IOManager::addDefaultMachineData(const std::string& machineName) {
    /*
    if (machineName == "Cavity") {
        std::vector<IOData> defaultData = {
            {"X1000", "Unit1_急停 EMO", "Y1010", "Unit1_四色灯-红"},
@@ -324,6 +326,7 @@
        };
        m_machines[machineName] = defaultData;
    }
    */
}
void IOManager::saveToFile(const std::string& machineName) {
@@ -356,6 +359,10 @@
        IOData entry;
        size_t pos = 0;
        pos = line.find(",");
        entry.bInputStates = FALSE;
        entry.bOutputStates = FALSE;
        entry.inputAddress = line.substr(0, pos);
        line.erase(0, pos + 1);
SourceCode/Bond/BondEq/FileManager/IOManager.h
@@ -6,8 +6,10 @@
#include <map>
struct IOData {
    BOOL bInputStates;
    std::string inputAddress;
    std::string inputDescription;
    BOOL bOutputStates;
    std::string outputAddress;
    std::string outputDescription;
};
SourceCode/Bond/BondEq/FileManager/RecipeManager.cpp
@@ -111,7 +111,7 @@
        axisInfo.id = axisId;
        axisInfo.number = "M100-M" + std::to_string(axisId);
        axisInfo.description = "Default_Axis" + std::to_string(axisId);
        axisInfo.startAddress = "D" + std::to_string(5000 + axisId * 10);
        axisInfo.startAddress = "ZR" + std::to_string(10000 + (axisId - 1) * 300);
        axisInfo.jogDistance = 0.5;
        axisInfo.manualSpeed = 10.0;
        axisInfo.autoSpeed = 15.0;
SourceCode/Bond/BondEq/ToolUnits.cpp
@@ -160,6 +160,41 @@
    return (pBuffer[0] & 0xff) | (pBuffer[1] & 0xff) << 8;
}
BOOL CToolUnits::getBit(const char c, int index)
{
    switch (index)
    {
    case 0:
        return c & 0x01;
        break;
    case 1:
        return c & 0x02;
        break;
    case 2:
        return c & 0x04;
        break;
    case 3:
        return c & 0x08;
        break;
    case 4:
        return c & 0x10;
        break;
    case 5:
        return c & 0x20;
        break;
    case 6:
        return c & 0x40;
        break;
    case 7:
        return c & 0x80;
        break;
    default:
        break;
    }
    return FALSE;
}
void CToolUnits::setDlgItemDouble(CWnd* pWnd, int nCtrlId, double value)
{
    CString strText;
SourceCode/Bond/BondEq/ToolUnits.h
@@ -22,6 +22,7 @@
    static bool isDirectory(const std::string& path);
    static double toInt32(const char* pBuffer);
    static double toInt16(const char* pBuffer);
    static BOOL getBit(const char c, int index);
    static void setDlgItemDouble(CWnd* pWnd, int nCtrlId, double value);
};
SourceCode/Bond/BondEq/View/AxisSettingsDlg.cpp
@@ -421,24 +421,87 @@
    }
}
bool CAxisSettingsDlg::ParsePLCAddress(const CString& address, MC::SOFT_COMPONENT& component, int& addr)
{
    if (address.GetLength() < 2) {
        return false;
    }
    // 提取组件类型(第一个字符)
    TCHAR componentChar = address[0];
    if (address.Left(2) == _T("ZR")) {
        component = MC::SOFT_COMPONENT::ZR;
        // 提取数字部分(去除ZR前缀)
        CString numericAddress = address.Mid(2);
        addr = _ttoi(numericAddress);
        return addr != 0 || numericAddress.CompareNoCase(_T("0")) == 0;  // 如果是 "0",也认为有效
    }
    // 对于其他组件,按照常规规则处理
    CString hexAddress = address.Mid(1);
    switch (componentChar) {
    case 'D':
        component = MC::SOFT_COMPONENT::D;
        addr = _ttoi(hexAddress);
        break;
    case 'M':
        component = MC::SOFT_COMPONENT::M;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'X':
        component = MC::SOFT_COMPONENT::X;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'Y':
        component = MC::SOFT_COMPONENT::Y;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'W':
        component = MC::SOFT_COMPONENT::W;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'L':
        component = MC::SOFT_COMPONENT::L;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'S':
        component = MC::SOFT_COMPONENT::S;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'B':
        component = MC::SOFT_COMPONENT::B;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'F':
        component = MC::SOFT_COMPONENT::F;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    default:
        return false;
    }
    // 检查地址是否有效
    if (addr == 0 && hexAddress.CompareNoCase(_T("0")) != 0) {
        return false;
    }
    return true;
}
void CAxisSettingsDlg::writeAxisDataToPLC(int nAxisId)
{
    // 从 RecipeManager 获取轴数据
    RecipeManager& recipeManager = RecipeManager::getInstance();
    auto axisData = recipeManager.getAxis(nAxisId);
    // 去除非数字字符并转换起始地址
    std::string cleanAddress = axisData.startAddress;
    cleanAddress.erase(std::remove_if(cleanAddress.begin(), cleanAddress.end(),
        [](char c) { return !std::isdigit(c); }), cleanAddress.end());
    if (cleanAddress.empty()) {
    int startAddress;
    MC::SOFT_COMPONENT component;
    if (!ParsePLCAddress(CString(axisData.startAddress.c_str()), component, startAddress)) {
        AfxMessageBox(_T("无效的起始地址!"));
        return;
    }
    int startAddress = std::stoi(cleanAddress);
    // 写入手动速度
    m_pPLC->writeWord(MC::SOFT_COMPONENT::D, 5120, (int)axisData.manualSpeed, [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
    m_pPLC->writeDWord(component, startAddress + 82, (int)axisData.manualSpeed * 1000, [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
        if (flag == 0) {
            TRACE("\n写入成功: 手动速度, 地址: %d, 值: %lu\n", addr, value);
        }
@@ -448,7 +511,7 @@
    });
    // 写入自动速度
    m_pPLC->writeWord(MC::SOFT_COMPONENT::D, startAddress + 2, (int)axisData.autoSpeed, [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
    m_pPLC->writeDWord(component, startAddress + 84, (int)(axisData.autoSpeed * 1000.0), [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
        if (flag == 0) {
            TRACE("\n写入成功: 自动速度, 地址: %d, 值: %lu\n", addr, value);
        }
@@ -458,7 +521,7 @@
    });
    // 写入加速时间, 转换为毫秒
    m_pPLC->writeWord(MC::SOFT_COMPONENT::D, startAddress + 4, (int)(axisData.accelerationTime * 1000), [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
    m_pPLC->writeDWord(component, startAddress + 62, (int)(axisData.accelerationTime * 1000.0), [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
        if (flag == 0) {
            TRACE("\n写入成功: 加速时间, 地址: %d, 值: %lu\n", addr, value);
        }
@@ -468,7 +531,7 @@
    });
    // 写入减速时间, 转换为毫秒
    m_pPLC->writeWord(MC::SOFT_COMPONENT::D, startAddress + 6, (int)(axisData.decelerationTime * 1000), [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
    m_pPLC->writeDWord(component, startAddress + 64, (int)(axisData.decelerationTime * 1000.0), [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
        if (flag == 0) {
            TRACE("\n写入成功: 减速时间, 地址: %d, 值: %lu\n", addr, value);
        }
@@ -478,7 +541,7 @@
    });
    // 写入微动量
    m_pPLC->writeWord(MC::SOFT_COMPONENT::D, startAddress + 8, (int)axisData.jogDistance, [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
    m_pPLC->writeWord(component, startAddress + 81, (int)(axisData.jogDistance * 1000.0), [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
        if (flag == 0) {
            TRACE("\n写入成功: 微动量, 地址: %d, 值: %lu\n", addr, value);
        }
@@ -488,12 +551,13 @@
    });
    // 写入定位点数据
    int positionStartAddress = startAddress + 10;
    /*
    int positionStartAddress = startAddress + 100;
    for (size_t i = 0; i < axisData.positions.size(); ++i) {
        const auto& position = axisData.positions[i];
        int positionAddress = positionStartAddress + (i * 2);
        unsigned int positionAddress = positionStartAddress + (i * 4);
        m_pPLC->writeWord(MC::SOFT_COMPONENT::D, positionAddress, (int)position.second, [i](IMcChannel* pChannel, int addr, DWORD value, int flag) {
        m_pPLC->writeWord(component, positionAddress, (int)position.second, [i](IMcChannel* pChannel, int addr, DWORD value, int flag) {
            if (flag == 0) {
                TRACE("\n写入成功: 定位点 %d, 地址: %d, 值: %lu\n", i + 1, addr, value);
            }
@@ -502,6 +566,7 @@
            }
        });
    }
    */
}
void CAxisSettingsDlg::handleAxisOperation(AxisOperationType eOpType, bool bPressed)
@@ -516,18 +581,14 @@
    RecipeManager& recipeManager = RecipeManager::getInstance();
    auto axisData = recipeManager.getAxis(nAxisId);
    std::string strCleanAddress = axisData.startAddress;
    strCleanAddress.erase(std::remove_if(strCleanAddress.begin(), strCleanAddress.end(),
        [](unsigned char c) { return !std::isdigit(c); }), strCleanAddress.end());
    if (strCleanAddress.empty()) {
    int startAddress;
    MC::SOFT_COMPONENT component;
    if (!ParsePLCAddress(CString(axisData.startAddress.c_str()), component, startAddress)) {
        AfxMessageBox(_T("无效的起始地址!"));
        return;
    }
    int nStartAddress = std::stoi(strCleanAddress);
    // 根据操作类型计算目标地址
    int nTargetAddress = nStartAddress;
    int nTargetAddress = startAddress + 10;
    switch (eOpType) {
    case AxisOperationType::OPR:
        nTargetAddress += 10; // OPR 信号地址
@@ -562,7 +623,7 @@
    }
    // 向 PLC 写入信号
    m_pPLC->writeBit(MC::SOFT_COMPONENT::D, nTargetAddress, bPressed, [eOpType, nTargetAddress, bPressed](IMcChannel* pChannel, int nAddr, DWORD nValue, int nFlag) {
    m_pPLC->writeBit(component, nTargetAddress, bPressed, [eOpType, nTargetAddress, bPressed](IMcChannel* pChannel, int nAddr, DWORD nValue, int nFlag) {
        if (nFlag == 0) {
            TRACE("操作成功:类型=%d,地址=%d,值=%d\n", static_cast<int>(eOpType), nAddr, bPressed);
        }
@@ -570,6 +631,45 @@
            TRACE("操作失败:类型=%d,地址=%d,错误码=%d\n", static_cast<int>(eOpType), nAddr, nFlag);
        }
    });
}
void CAxisSettingsDlg::readPLCDataToUI(int nAxisId)
{
    CBLLabel* pLabels[] = { &m_staticFLS, &m_staticDOG, &m_staticRLS, &m_staticReady, &m_staticBusy, &m_staticErr };
    // 从 RecipeManager 获取轴数据
    RecipeManager& recipeManager = RecipeManager::getInstance();
    auto axisData = recipeManager.getAxis(nAxisId);
    MC::SOFT_COMPONENT component;
    int startAddress, endAddress, readSize;
    if (!ParsePLCAddress(CString(axisData.startAddress.c_str()), component, startAddress)) {
        AfxMessageBox(_T("无效的起始地址!"));
    }
    // 从 OPR 信号地址开始读取
    startAddress += 10;
    endAddress = startAddress + 24;
    readSize = endAddress - startAddress + 1;
    // 回调处理输入数据
    auto funOnReadData = [this, startAddress, &pLabels](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) {
        if (flag == 0) {
            int nOffset = 0;
            for (auto pLabel : pLabels) {
                int value = CToolUnits::toInt16(&pData[(startAddress + nOffset) * 2]);
                if (value == 0) {
                    SetStatusColor(*pLabel, FALSE);
                } else {
                    SetStatusColor(*pLabel, TRUE);
                }
                nOffset++;
            }
        }
    };
    m_pPLC->readData(component, startAddress, readSize, funOnReadData);
}
@@ -925,6 +1025,11 @@
    if (TIMER_READ_PLC_DATA == nIDEvent) {
        ASSERT(m_pPLC);
        int nAxisId = getCurrentSelectedAxisID();
        if (nAxisId == -1) {
            return;
        }
        int addr1, addr2, readSize;
        addr1 = 5120;
        addr2 = 5425;
@@ -947,7 +1052,9 @@
                SetDlgItemInt(IDC_EDIT_AXIS_CURR_ALARM_NUMBER, nAlarmCode);
            }
        };
        m_pPLC->readData(MC::SOFT_COMPONENT::D, addr1, readSize, funOnReadData);
        //m_pPLC->readData(MC::SOFT_COMPONENT::D, addr1, readSize, funOnReadData);
        //readPLCDataToUI(nAxisId);
    }    
    else if (nIDEvent == TIMER_JOG_ADD && m_bJogAddPressed) {
        TRACE("持续发送 JOG+\n");
SourceCode/Bond/BondEq/View/AxisSettingsDlg.h
@@ -51,9 +51,10 @@
    void updateAxisSelection(int offset);
    void updateDataFromUI(int nAxisId);
    void switchToPage(int targetPage);
    bool ParsePLCAddress(const CString& address, MC::SOFT_COMPONENT& component, int& addr);
    void writeAxisDataToPLC(int nAxisId);
    void handleAxisOperation(AxisOperationType eOpType, bool bPressed);
    void readPLCDataToUI(int nAxisId);
private:
    CPLC* m_pPLC;
SourceCode/Bond/BondEq/View/IOMonitoringDlg.cpp
@@ -9,6 +9,12 @@
#define TIMER_INIT                1
#define TIMER_READ_PLC_DATA        2
#define TIMER_READ_UPDATE        3
#define LABEL_ID_BEGIN            2000
#define    ADDR_WND                _T("ADDR_WND")
#define ID_MSG_IO_CLICK            WM_USER + 101
// CIOMonitoringDlg 对话框
@@ -170,65 +176,40 @@
    int colWidthLarge = availableWidth * 4 / 14;        // 大宽度列比例
    int groupWidth = colWidthSmall * 2 + colWidthLarge; // 每组总宽度
    UINT id = LABEL_ID_BEGIN;
    for (int i = 0; i < m_nRowsPerPage; ++i) {
        // 每一行的起始 Y 坐标
        int y = topMargin + i * (rowHeight + verticalSpacing);
        // 创建第 1 组 (0, 1, 2)
        int x = sideMargin; // 从左边距开始
        CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter);
        CreateStaticControl(++id, x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter);
        x += colWidthSmall;
        CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("X1000"), false, AlignCenter);
        CreateStaticControl(++id, x, y, colWidthSmall, rowHeight, _T("X1000"), false, AlignCenter);
        x += colWidthSmall;
        CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false);
        CreateStaticControl(++id, x, y, colWidthLarge, rowHeight, _T("描述文本"), false);
        // 第 2 组起始位置,加上组间距
        x += colWidthLarge + groupSpacing;
        // 创建第 2 组 (3, 4, 5)
        CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter, [this, i]() {
        id++;
        CWnd* pBtn = CreateStaticControl(id, x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter, [this, id, i, x, y]() {
            // 自定义点击事件的逻辑
            auto* pControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 3]);
            CString currentText;
            pControl->GetWindowText(currentText);
            BOOL bOn = FALSE;
            if (currentText == _T("OFF")) {
                //pControl->SetBkColor(RGB(0, 255, 0)); // 绿色背景
                //pControl->SetText(_T("ON"));          // 更新文本为 ON
                bOn = TRUE;
            }
            else {
                //pControl->SetBkColor(RGB(255, 0, 0)); // 红色背景
                //pControl->SetText(_T("OFF"));         // 更新文本为 OFF
                bOn = FALSE;
            }
            pControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 4]);
            pControl->GetWindowText(currentText);
            int nAddress;
            MC::SOFT_COMPONENT component;
            if (ParsePLCAddress(currentText, component, nAddress) && m_pPLC) {
                TRACE("地址解析成功: %s\n", currentText);
                m_pPLC->writeBit(component, nAddress, bOn, [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
                    if (flag == 0) {
                        TRACE("写入成功: 地址: %d, 值: %lu\n", addr, value);
                    }
                    else {
                        TRACE("写入失败: 地址: %d, 错误码: %d\n", addr, flag);
                    }
                });
            }
            PostMessage(ID_MSG_IO_CLICK, id, currentText == _T("OFF") ? 1 : 0);
        });
        x += colWidthSmall;
        CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("Y1010"), false, AlignCenter);
        CWnd* pLabel = CreateStaticControl(++id, x, y, colWidthSmall, rowHeight, _T("Y1010"), false, AlignCenter);
        x += colWidthSmall;
        CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false);
        CreateStaticControl(++id, x, y, colWidthLarge, rowHeight, _T("描述文本"), false);
        ::SetProp(pBtn->GetSafeHwnd(), ADDR_WND, pLabel);
    }
}
void CIOMonitoringDlg::CreateStaticControl(int x, int y, int width, int height, const CString& text, bool hasBorder, TextAlign alignment, std::function<void()> clickCallback)
CWnd* CIOMonitoringDlg::CreateStaticControl(UINT id, int x, int y, int width, int height, const CString& text, bool hasBorder, TextAlign alignment, std::function<void()> clickCallback)
{
    // 创建动态控件
    CBLLabel* pStatic = new CBLLabel();
@@ -236,7 +217,7 @@
    if (hasBorder) {
        style |= WS_BORDER; // 添加边框
    }
    pStatic->Create(text, style, CRect(x, y, x + width, y + height), this);
    pStatic->Create(text, style, CRect(x, y, x + width, y + height), this, id);
    // 设置文本对齐方式
    pStatic->SetAlignment(alignment);
@@ -255,6 +236,33 @@
    // 存储控件指针
    m_staticControls.push_back(pStatic);
    return pStatic;
}
CWnd* CIOMonitoringDlg::GetStaticControl(UINT id)
{
    for (auto item : m_staticControls) {
        TRACE(">>id:%d\n", item->GetDlgCtrlID());
        if (item->GetDlgCtrlID() == id) {
            return item;
        }
    }
    return nullptr;
}
CString& CIOMonitoringDlg::GetStaticControlAddrText(UINT id, CString& strAddr)
{
    CWnd* pBtn = GetStaticControl(id);
    if (pBtn != nullptr) {
        CWnd* pLabel = (CWnd*)::GetProp(pBtn->GetSafeHwnd(), ADDR_WND);
        if (pLabel != nullptr) {
            pLabel->GetWindowText(strAddr);
        }
    }
    return strAddr;
}
void CIOMonitoringDlg::DisplayCurrentPage()
@@ -326,109 +334,200 @@
    // 提取组件类型(第一个字符)
    TCHAR componentChar = address[0];
    if (address.Left(2) == _T("ZR")) {
        component = MC::SOFT_COMPONENT::ZR;
        // 提取数字部分(去除ZR前缀)
        CString numericAddress = address.Mid(2);
        addr = _tcstoul(numericAddress, nullptr, 16);
        return addr != 0 || numericAddress.CompareNoCase(_T("0")) == 0;  // 如果是 "0",也认为有效
    }
    // 对于其他组件,按照常规规则处理
    CString hexAddress = address.Mid(1);
    switch (componentChar) {
    case 'D':
        component = MC::SOFT_COMPONENT::D;
        addr = _ttoi(hexAddress);
        break;
    case 'M':
        component = MC::SOFT_COMPONENT::M;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'X':
        component = MC::SOFT_COMPONENT::X;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'Y':
        component = MC::SOFT_COMPONENT::Y;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'W':
        component = MC::SOFT_COMPONENT::W;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'L':
        component = MC::SOFT_COMPONENT::L;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'S':
        component = MC::SOFT_COMPONENT::S;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'B':
        component = MC::SOFT_COMPONENT::B;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    case 'F':
        component = MC::SOFT_COMPONENT::F;
        addr = _tcstoul(hexAddress, nullptr, 16);
        break;
    default:
        return false;
    }
    CString hexAddress = address.Mid(1);
    addr = _tcstoul(hexAddress, nullptr, 16);
    // 检查地址是否有效
    if (addr == 0 && hexAddress.CompareNoCase(_T("0")) != 0) {
        return false;
    }
    return true;
}
void CIOMonitoringDlg::UpdatePLCStates()
bool CIOMonitoringDlg::GeneratePLCAddress(MC::SOFT_COMPONENT component, int addr, CString& address, bool bHexFormat)
{
    // 示例:从 PLC 获取值,这里用随机值模拟
    //for (size_t i = 0; i < m_inputPLCAddresses.size(); ++i) {
    //    // 模拟获取输入状态
    //    bool inputState = (rand() % 2 == 0); // 偶尔为 true/false
    //    auto* inputControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 0]);
    //    inputControl->SetBkColor(inputState ? RGB(0, 255, 0) : RGB(255, 0, 0));
    //    inputControl->SetText(inputState ? _T("ON") : _T("OFF"));
    //}
    //for (size_t i = 0; i < m_outputPLCAddresses.size(); ++i) {
    //    // 模拟获取输出状态
    //    bool outputState = (rand() % 2 == 0); // 偶尔为 true/false
    //    auto* outputControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 3]);
    //    outputControl->SetBkColor(outputState ? RGB(0, 255, 0) : RGB(255, 0, 0));
    //    outputControl->SetText(outputState ? _T("ON") : _T("OFF"));
    //}
    // 输入地址的读取
    if (!m_inputPLCAddresses.empty()) {
        // 获取起始地址和长度
        CString startAddressStr = m_inputPLCAddresses.front();
        CString endAddressStr = m_inputPLCAddresses.back();
        MC::SOFT_COMPONENT component;
        int startAddress, endAddress;
        // 解析起始和结束地址
        if (ParsePLCAddress(startAddressStr, component, startAddress) &&
            ParsePLCAddress(endAddressStr, component, endAddress)) {
            int inputSize = endAddress - startAddress + 1;
            // 回调处理输入数据
            auto funOnReadInput = [this, startAddress](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) {
                if (nDataSize == (unsigned int)(m_inputPLCAddresses.size()) && flag == 0) {
                    for (size_t i = 0; i < m_inputPLCAddresses.size(); ++i) {
                        int offset = i;
                        int value = CToolUnits::toInt16(&pData[offset]);
                        auto* inputControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 0]); // 第 0 列
                        inputControl->SetBkColor(value ? RGB(0, 255, 0) : RGB(255, 0, 0)); // 更新背景颜色
                        inputControl->SetText(value ? _T("ON") : _T("OFF"));               // 更新文本
                    }
                }
            };
            // 读取输入数据
            m_pPLC->readData(component, startAddress, inputSize, funOnReadInput);
        }
    // 根据组件类型生成前缀
    CString prefix;
    switch (component) {
    case MC::SOFT_COMPONENT::D:
        prefix = _T("D");
        break;
    case MC::SOFT_COMPONENT::M:
        prefix = _T("M");
        break;
    case MC::SOFT_COMPONENT::X:
        prefix = _T("X");
        break;
    case MC::SOFT_COMPONENT::Y:
        prefix = _T("Y");
        break;
    case MC::SOFT_COMPONENT::W:
        prefix = _T("W");
        break;
    case MC::SOFT_COMPONENT::L:
        prefix = _T("L");
        break;
    case MC::SOFT_COMPONENT::S:
        prefix = _T("S");
        break;
    case MC::SOFT_COMPONENT::B:
        prefix = _T("B");
        break;
    case MC::SOFT_COMPONENT::F:
        prefix = _T("F");
        break;
    case MC::SOFT_COMPONENT::ZR:
        prefix = _T("ZR");
        break;
    default:
        return false;  // 如果是未知组件类型,返回失败
    }
    // 输出地址的读取
    if (!m_outputPLCAddresses.empty()) {
        // 获取起始地址和长度
        CString startAddressStr = m_outputPLCAddresses.front();
        CString endAddressStr = m_outputPLCAddresses.back();
        MC::SOFT_COMPONENT component;
        int startAddress, endAddress;
    // 根据 bHexFormat 判断输出格式
    CString strAddr;
    if (bHexFormat) {
        strAddr.Format(_T("%X"), addr);  // 十六进制格式
    }
    else {
        strAddr.Format(_T("%d"), addr);  // 十进制格式
    }
        // 解析起始和结束地址
        if (ParsePLCAddress(startAddressStr, component, startAddress) &&
            ParsePLCAddress(endAddressStr, component, endAddress)) {
            int outputSize = endAddress - startAddress + 1;
    // 生成最终的地址字符串
    address = prefix + strAddr;
    return true;
}
            // 回调处理输出数据
            auto funOnReadOutput = [this, startAddress](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) {
                if (nDataSize == (unsigned int)(m_outputPLCAddresses.size()) && flag == 0) {
                    for (size_t i = 0; i < m_outputPLCAddresses.size(); ++i) {
                        int offset = i;
                        int value = CToolUnits::toInt16(&pData[offset]);
// 处理PLC数据读取的通用方法
void CIOMonitoringDlg::ReadPLCData(MC::SOFT_COMPONENT softComponent, int startAddr, int endAddr, std::function<void(IMcChannel*, int, char*, unsigned int, int)> callback)
{
    int nSize;
    // 检查地址是否有效,以及组件是否匹配
    nSize = endAddr - startAddr + 1;
    if (nSize < 1) {
        return;
    }
                        auto* outputControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 3]); // 第 3 列
                        outputControl->SetBkColor(value ? RGB(0, 255, 0) : RGB(255, 0, 0)); // 更新背景颜色
                        outputControl->SetText(value ? _T("ON") : _T("OFF"));               // 更新文本
                    }
    // 读取数据
    m_pPLC->readData(softComponent, startAddr, nSize, callback);
}
void CIOMonitoringDlg::ReadPLCStates()
{
    if (m_displayData.empty()) {
        return;
    }
    auto startData = m_displayData.front();
    auto endData = m_displayData.back();
    MC::SOFT_COMPONENT startSoftComponent, endSoftComponent;
    int startAddr, endAddr;
    ParsePLCAddress(CString(startData.inputAddress.c_str()), startSoftComponent, startAddr);
    ParsePLCAddress(CString(endData.inputAddress.c_str()), endSoftComponent, endAddr);
    ASSERT(startSoftComponent == endSoftComponent);
    // 读取输入数据
    ReadPLCData(startSoftComponent, startAddr, endAddr, [this, startAddr](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) {
        for (auto& data : m_displayData) {
            int nAddress;
            MC::SOFT_COMPONENT enComponent;
            if (ParsePLCAddress(CString(data.inputAddress.c_str()), enComponent, nAddress)) {
                int nOffset = nAddress - startAddr;
                if (nOffset >= 0 && nOffset < nDataSize) {
                    int byteIndex = nOffset / 8;
                    int bitIndex = nOffset % 8;
                    data.bInputStates = CToolUnits::getBit(pData[byteIndex], bitIndex);
                }
            };
            }
        }
    });
            // 读取输出数据
            m_pPLC->readData(component, startAddress, outputSize, funOnReadOutput);
    // 读取输出数据
    int startAddr2, endAddr2;
    ParsePLCAddress(CString(startData.outputAddress.c_str()), startSoftComponent, startAddr2);
    ParsePLCAddress(CString(endData.outputAddress.c_str()), endSoftComponent, endAddr2);
    ASSERT(startSoftComponent == endSoftComponent);
    ReadPLCData(startSoftComponent, startAddr2, endAddr2, [this, startAddr2](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) {
        for (auto& data : m_displayData) {
            int nAddress;
            MC::SOFT_COMPONENT enComponent;
            if (ParsePLCAddress(CString(data.outputAddress.c_str()), enComponent, nAddress)) {
                int nOffset = nAddress - startAddr2;
                if (nOffset >= 0 && nOffset < nDataSize) {
                    int byteIndex = nOffset / 8;
                    int bitIndex = nOffset % 8;
                    data.bOutputStates = CToolUnits::getBit(pData[byteIndex], bitIndex);
                    TRACE(">>>> %d %d %d |||| %d\n", nOffset, byteIndex, bitIndex, data.bOutputStates);
                }
            }
        }
    });
}
void CIOMonitoringDlg::UpdatePLCStatesToUI()
{
    int startIndex = (m_nCurrentPage - 1) * m_nRowsPerPage;
    int endIndex = min(startIndex + m_nRowsPerPage, static_cast<int>(m_displayData.size()));
    for (int i = 0; i < m_nRowsPerPage; ++i) {
        int row = i;
        if (startIndex + i < endIndex) {
            const auto& data = m_displayData[startIndex + i];
            // 设置内容和背景颜色
            m_staticControls[row * m_nCols + 0]->SetText(data.bInputStates ? _T("ON") : _T("OFF"));
            m_staticControls[row * m_nCols + 0]->SetBkColor(data.bInputStates ? RGB(0, 255, 0) : RGB(255, 0, 0));
            m_staticControls[row * m_nCols + 3]->SetText(data.bOutputStates ? _T("ON") : _T("OFF"));
            m_staticControls[row * m_nCols + 3]->SetBkColor(data.bOutputStates ? RGB(0, 255, 0) : RGB(255, 0, 0));
        }
    }
}
@@ -436,6 +535,7 @@
BEGIN_MESSAGE_MAP(CIOMonitoringDlg, CDialogEx)
    ON_BN_CLICKED(IDC_BUTTON_PREV_PAGE, &CIOMonitoringDlg::OnBnClickedButtonPrevPage)
    ON_BN_CLICKED(IDC_BUTTON_NEXT_PAGE, &CIOMonitoringDlg::OnBnClickedButtonNextPage)
    ON_MESSAGE(ID_MSG_IO_CLICK, &CIOMonitoringDlg::OnIoClicked)
    ON_WM_SIZE()
    ON_WM_TIMER()
    ON_WM_CLOSE()
@@ -502,10 +602,9 @@
    CreateDynamicControls();
    DisplayCurrentPage();
    GetDlgItem(IDC_BUTTON_PREV_PAGE)->EnableWindow(m_nCurrentPage > 1);
    GetDlgItem(IDC_BUTTON_NEXT_PAGE)->EnableWindow(m_nCurrentPage < m_nTotalPages);
    SetTimer(TIMER_READ_PLC_DATA, 500, nullptr);
    SetTimer(TIMER_READ_UPDATE, 200, nullptr);
    return TRUE;  // return TRUE unless you set the focus to a control
    // 异常: OCX 属性页应返回 FALSE
@@ -537,9 +636,6 @@
    else {
        AfxMessageBox(_T("已经是第一页!"));
    }
    GetDlgItem(IDC_BUTTON_PREV_PAGE)->EnableWindow(m_nCurrentPage > 1);
    GetDlgItem(IDC_BUTTON_NEXT_PAGE)->EnableWindow(m_nCurrentPage < m_nTotalPages);
}
void CIOMonitoringDlg::OnBnClickedButtonNextPage()
@@ -552,9 +648,30 @@
    else {
        AfxMessageBox(_T("已经是最后一页!"));
    }
}
    GetDlgItem(IDC_BUTTON_PREV_PAGE)->EnableWindow(m_nCurrentPage > 1);
    GetDlgItem(IDC_BUTTON_NEXT_PAGE)->EnableWindow(m_nCurrentPage < m_nTotalPages);
LRESULT CIOMonitoringDlg::OnIoClicked(WPARAM wParam, LPARAM lParam)
{
    CString strAddr;
    GetStaticControlAddrText((UINT)wParam, strAddr);
    BOOL bOn = (BOOL)lParam;
    int nAddress;
    MC::SOFT_COMPONENT component;
    if (ParsePLCAddress(strAddr, component, nAddress) && m_pPLC) {
        TRACE("地址解析成功: %s %d\n", strAddr, GetCurrentThreadId());
        int n = m_pPLC->writeBit(component, nAddress, bOn, [](IMcChannel* pChannel, int addr, DWORD value, int flag) {
            if (flag == 0) {
                TRACE("写入成功: 地址: %d, 值: %lu\n", addr, value);
            }
            else {
                TRACE("写入失败: 地址: %d, 错误码: %d\n", addr, flag);
            }
            });
        TRACE("地址解析成功2: %d\n", n);
    }
    return 0;
}
void CIOMonitoringDlg::OnTimer(UINT_PTR nIDEvent)
@@ -562,9 +679,13 @@
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    if (TIMER_READ_PLC_DATA == nIDEvent) {
        ASSERT(m_pPLC);
        UpdatePLCStates();
        Sleep(100);
        ReadPLCStates();
    }
    else if (TIMER_READ_UPDATE == nIDEvent) {
        ASSERT(m_pPLC);
        UpdatePLCStatesToUI();
    }
    CDialogEx::OnTimer(nIDEvent);
}
@@ -572,5 +693,6 @@
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    KillTimer(TIMER_READ_PLC_DATA);
    KillTimer(TIMER_READ_UPDATE);
    CDialogEx::OnClose();
}
SourceCode/Bond/BondEq/View/IOMonitoringDlg.h
@@ -28,13 +28,18 @@
    void AdjustControls(float dScaleX, float dScaleY);                // 调整控件大小
    void AdjustControlFont(CWnd* pWnd, int nWidth, int nHeight);    // 调整控件字体大小
    void UpdatePageInfo();            // 更新分页信息
    void CreateDynamicControls();    // 动态创建控件
    void CreateStaticControl(int x, int y, int width, int height, const CString& text, bool hasBorder = false, TextAlign alignment = AlignLeft, std::function<void()> clickCallback = nullptr); // 创建静态控件
    void DisplayCurrentPage();        // 显示当前页数据
    void ClearDynamicControls();    // 清除动态创建的控件
    void UpdatePageInfo();             // 更新分页信息
    void CreateDynamicControls();     // 动态创建控件
    CWnd* CreateStaticControl(UINT id, int x, int y, int width, int height, const CString& text, bool hasBorder = false, TextAlign alignment = AlignLeft, std::function<void()> clickCallback = nullptr); // 创建静态控件
    CWnd* GetStaticControl(UINT id); // 获取静态控件
    CString& GetStaticControlAddrText(UINT id, CString& strAddr); // 获取静态控件地址文本
    void DisplayCurrentPage();         // 显示当前页数据
    void ClearDynamicControls();     // 清除动态创建的控件
    bool ParsePLCAddress(const CString& address, MC::SOFT_COMPONENT& component, int& addr); // 解析 PLC 地址
    void UpdatePLCStates();            // 定时器更新状态的方法
    bool GeneratePLCAddress(MC::SOFT_COMPONENT component, int addr, CString& address, bool bHexFormat = false); // 生成 PLC 地址
    void ReadPLCData(MC::SOFT_COMPONENT softComponent, int startAddr, int endAddr, std::function<void(IMcChannel*, int, char*, unsigned int, int)> callback); // 读取 PLC 数据
    void ReadPLCStates();             // 定时器读PLC
    void UpdatePLCStatesToUI();         // 定时器更新状态的方法
private:
    CPLC* m_pPLC;
@@ -64,6 +69,7 @@
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnBnClickedButtonPrevPage();
    afx_msg void OnBnClickedButtonNextPage();
    afx_msg LRESULT OnIoClicked(WPARAM wParam, LPARAM lParam);
    afx_msg void OnTimer(UINT_PTR nIDEvent);
    afx_msg void OnClose();
};