mrDarker
2025-06-06 ecd10e09a6d85b77204814b813c62691a9deec39
1. 通过文件读取多个设备的信号,并显示在界面
已修改2个文件
185 ■■■■ 文件已修改
SourceCode/Bond/Servo/CEquipmentPage1.cpp 176 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipmentPage1.h 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CEquipmentPage1.cpp
@@ -19,6 +19,7 @@
    : CHMPropertyPage(IDD_PAGE_EQUIPMENT1, pParent)
{
    m_pEquipment = nullptr;
    m_nCurrentDeviceID = 0;
}
CEquipmentPage1::~CEquipmentPage1()
@@ -47,54 +48,137 @@
void CEquipmentPage1::setEquipment(SERVO::CEquipment* pEquipment)
{
    m_pEquipment = pEquipment;
    if (m_pEquipment != nullptr) {
        InitSignalListForDevice(pEquipment->getID());
        LoadSignalPanelUI();
    }
    else {
        m_nCurrentDeviceID = 0;
        m_vSignalList.clear();
        m_ctrlSignalPanel.ClearAll();
    }
}
void CEquipmentPage1::InitSignalList()
void CEquipmentPage1::LoadSignalPanelUI()
{
    if (!::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) {
        return;
    }
    for (int i = 0; i < SIGNAL_GRID_SIZE; ++i) {
        int nRow = i / SIGNAL_GRID_COLS;
        int nCol = i % SIGNAL_GRID_COLS;
        const auto& signal = m_vSignalList[i];
        m_ctrlSignalPanel.SetSlotText(nRow, nCol, signal.strName);
        m_ctrlSignalPanel.SetSlotClickable(nRow, nCol, signal.bClickable);
    }
}
void CEquipmentPage1::LoadSignalListFromCSV(const CString& strFilePath)
{
    m_mapSignalListByID.clear();
    CStdioFile file;
    if (!file.Open(strFilePath, CFile::modeRead | CFile::typeText)) {
        AfxMessageBox(_T("无法打开信号定义文件: ") + strFilePath);
        return;
    }
    CString strLine;
    int nLineNum = 0;
    while (file.ReadString(strLine)) {
        ++nLineNum;
        strLine.Trim();
        if (strLine.IsEmpty() || strLine.Left(1) == _T("#") || strLine.Left(2) == _T("//")) {
            continue; // 跳过注释行或空行
        }
        // 分割为 tokens
        std::vector<CString> tokens;
        int curPos = 0;
        CString token = strLine.Tokenize(_T(","), curPos);
        while (!token.IsEmpty()) {
            token.Trim(); // 去除每个字段的空格
            tokens.push_back(token);
            token = strLine.Tokenize(_T(","), curPos);
        }
        // 格式检查
        if (tokens.size() < 3) {
            TRACE(_T("CSV 格式错误 [行 %d]:字段数不足。\n"), nLineNum);
            continue;
        }
        // 解析字段
        int nDeviceID = _ttoi(tokens[0]);
        CString strSignalName = tokens[1];
        bool bClickable = _ttoi(tokens[2]) ? TRUE : FALSE;
        if (nDeviceID <= 0 || strSignalName.IsEmpty()) {
            TRACE(_T("CSV 数据无效 [行 %d]:设备ID或信号名为空。\n"), nLineNum);
            continue;
        }
        SignalInfo info = { strSignalName, false, bClickable };
        m_mapSignalListByID[nDeviceID].push_back(info);
    }
    file.Close();
    TRACE(_T("信号定义加载完成,共 %d 个设备。\n"), (int)m_mapSignalListByID.size());
}
void CEquipmentPage1::InitSignalListForDevice(int nDeviceID)
{
    m_nCurrentDeviceID = nDeviceID;
    m_vSignalList.clear();
    // 写入设备的信号名称,默认状态为 false,默认都可点击(输出)
    m_vSignalList.push_back({ _T("Upstream\r\nInline"), false, true });
    m_vSignalList.push_back({ _T("Upstream\r\nTrouble"), false, true });
    m_vSignalList.push_back({ _T("Interlock"), false, false });
    m_vSignalList.push_back({ _T("Send\r\nAble"), false, true });
    m_vSignalList.push_back({ _T("Receive\r\nStart"), false, false });
    m_vSignalList.push_back({ _T("Job\r\nTransfer"), false, false });
    m_vSignalList.push_back({ _T("Receive\r\nComplete"), false, false });
    m_vSignalList.push_back({ _T("Shutter\r\nState"), false, false });
    // TODO: 添加更多信号,最多 64 个
    auto it = m_mapSignalListByID.find(nDeviceID);
    if (it != m_mapSignalListByID.end()) {
        m_vSignalList = it->second;
        UpdateAllSignalStatesFromDevice();
    }
    else {
        TRACE(_T("Warning: No signals found for DeviceID=%d\n"), nDeviceID);
    }
    // 补齐至 SIGNAL_GRID_SIZE 项
    // 补齐到 64 项
    while (m_vSignalList.size() < SIGNAL_GRID_SIZE) {
        m_vSignalList.push_back({ _T(""), false, false });
    }
}
void CEquipmentPage1::LoadDeviceSignals()
void CEquipmentPage1::UpdateSignalState(int nRow, int nCol, bool bNewState)
{
    m_ctrlSignalPanel.SetGridSize(SIGNAL_GRID_ROWS, SIGNAL_GRID_COLS);
    for (int i = 0; i < SIGNAL_GRID_SIZE; ++i) {
        int row = i / SIGNAL_GRID_COLS;
        int col = i % SIGNAL_GRID_COLS;
        const auto& signal = m_vSignalList[i];
        // 显示状态(用颜色表示 ON/OFF)
        m_ctrlSignalPanel.SetSlotText(row, col, signal.strName);
        m_ctrlSignalPanel.SetSlotStatus(row, col, signal.bCurrentState);
        m_ctrlSignalPanel.SetSlotClickable(row, col, signal.bClickable);
        m_ctrlSignalPanel.SetTextFont(_T("Microsoft YaHei"), 10);
    if (!::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) {
        return;
    }
    m_ctrlSignalPanel.SetSlotClickCallback([this](int nRow, int nCol) {
        int index = nRow * SIGNAL_GRID_COLS + nCol;
        if (index >= 0 && index < (int)m_vSignalList.size() && m_vSignalList[index].bClickable) {
            CString strMsg;
            strMsg.Format(_T("点击信号:%s"), m_vSignalList[index].strName);
            AfxMessageBox(strMsg);
    int nIndex = nRow * SIGNAL_GRID_COLS + nCol;
    if (nIndex < 0 || nIndex >= static_cast<int>(m_vSignalList.size())) {
        return;
        }
    });
    m_vSignalList[nIndex].bCurrentState = bNewState;
    m_ctrlSignalPanel.SetSlotStatus(nRow, nCol, bNewState);
    TRACE(_T("UpdateSignalState: [%d, %d] = %d\n"), nRow, nCol, bNewState);
}
void CEquipmentPage1::UpdateAllSignalStatesFromDevice()
{
    if (!m_pEquipment) {
        return;
    }
    for (int nRow = 0; nRow < SIGNAL_GRID_ROWS; ++nRow) {
        for (int nCol = 0; nCol < SIGNAL_GRID_COLS; ++nCol) {
            BOOL bCurrentState = m_pEquipment->isLinkSignalOn(nRow, nCol);
            UpdateSignalState(nRow, nCol, bCurrentState);
        }
    }
}
BOOL CEquipmentPage1::OnInitDialog()
@@ -104,9 +188,30 @@
    // TODO:  在此添加额外的初始化
    m_ctrlSignalPanel.Create(AfxRegisterWndClass(0), _T("SignalGrid"), WS_CHILD | WS_VISIBLE, CRect(0, 0, 100, 100), this, 1002);
    m_ctrlSignalPanel.SetColors(RGB(0, 200, 0), RGB(220, 220, 220));
    m_ctrlSignalPanel.SetGridSize(SIGNAL_GRID_ROWS, SIGNAL_GRID_COLS);
    m_ctrlSignalPanel.SetTextFont(_T("Microsoft YaHei"), 10);
    m_ctrlSignalPanel.SetSlotClickCallback([this](int nRow, int nCol) {
        int index = nRow * SIGNAL_GRID_COLS + nCol;
        if (index >= 0 && index < (int)m_vSignalList.size() && m_vSignalList[index].bClickable && m_pEquipment != nullptr) {
            // 读取当前状态并切换
            const BOOL bCurrentState = m_pEquipment->isLinkSignalOn(nRow, nCol);
            m_pEquipment->setLinkSignal(nRow, nCol, !bCurrentState);
        }
    });
    InitSignalList();
    LoadDeviceSignals();
    {
        TCHAR szPath[MAX_PATH] = {};
        GetModuleFileName(nullptr, szPath, MAX_PATH);
        PathRemoveFileSpec(szPath);
        CString strCSVFile = CString(szPath) + _T("\\Config\\signals.csv");
        LoadSignalListFromCSV(strCSVFile);
    }
    // 如果设备已设置,则初始化信号列表
    if (m_pEquipment != nullptr) {
        InitSignalListForDevice(m_pEquipment->getID());
        LoadSignalPanelUI();
    }
    return TRUE;  // return TRUE unless you set the focus to a control
                  // 异常: OCX 属性页应返回 FALSE
@@ -127,6 +232,7 @@
    CHMPropertyPage::OnDestroy();
    // TODO: 在此处添加消息处理程序代码
    m_ctrlSignalPanel.DestroyWindow();
}
void CEquipmentPage1::OnSize(UINT nType, int cx, int cy)
SourceCode/Bond/Servo/CEquipmentPage1.h
@@ -24,12 +24,17 @@
    void setEquipment(SERVO::CEquipment* pEquipment);
private:
    int m_nCurrentDeviceID;
    SERVO::CEquipment* m_pEquipment;
    std::vector<SignalInfo> m_vSignalList;
    CJobSlotGrid m_ctrlSignalPanel;
    std::map<int, std::vector<SignalInfo>> m_mapSignalListByID;
    void InitSignalList();
    void LoadDeviceSignals();
    void LoadSignalPanelUI();
    void LoadSignalListFromCSV(const CString& strFilePath);
    void InitSignalListForDevice(int nDeviceID);
    void UpdateSignalState(int nRow, int nCol, bool bNewState);
    void UpdateAllSignalStatesFromDevice();
// 对话框数据