// IOMonitoringDlg.cpp: 实现文件
|
//
|
|
#include "stdafx.h"
|
#include "BondEq.h"
|
#include "afxdialogex.h"
|
#include "IOMonitoringDlg.h"
|
#include "ToolUnits.h"
|
|
#define TIMER_INIT 1
|
#define TIMER_READ_PLC_DATA 2
|
|
// CIOMonitoringDlg 对话框
|
|
IMPLEMENT_DYNAMIC(CIOMonitoringDlg, CDialogEx)
|
|
CIOMonitoringDlg::CIOMonitoringDlg(CWnd* pParent /*=nullptr*/)
|
: CDialogEx(IDD_DIALOG_IO_MONITORING, pParent)
|
{
|
m_nCurrentPage = 1;
|
m_nTotalPages = 1;
|
m_nRowsPerPage = 10;
|
m_nCols = 6;
|
}
|
|
CIOMonitoringDlg::~CIOMonitoringDlg()
|
{
|
for (auto& pair : m_mapFonts) {
|
if (pair.second) {
|
pair.second->DeleteObject();
|
delete pair.second;
|
}
|
}
|
m_mapFonts.clear();
|
|
ClearDynamicControls();
|
}
|
|
void CIOMonitoringDlg::DoDataExchange(CDataExchange* pDX)
|
{
|
CDialogEx::DoDataExchange(pDX);
|
DDX_Control(pDX, IDC_STATIC_PAGE_NUMBER, m_staticPageNum);
|
}
|
|
void CIOMonitoringDlg::SetIOManager(const std::string& machineName)
|
{
|
IOManager manager;
|
manager.loadFromFile(machineName);
|
m_machineName = machineName;
|
|
// 加载数据
|
m_displayData = manager.GetMachineData(machineName);
|
|
// 计算页数
|
m_nCurrentPage = 1;
|
m_nTotalPages = (m_displayData.size() + m_nRowsPerPage - 1) / m_nRowsPerPage;
|
}
|
|
void CIOMonitoringDlg::SetPLC(CPLC* pPLC)
|
{
|
ASSERT(pPLC);
|
m_pPLC = pPLC;
|
}
|
|
CFont* CIOMonitoringDlg::GetOrCreateFont(int nFontSize)
|
{
|
auto it = m_mapFonts.find(nFontSize);
|
if (it != m_mapFonts.end()) {
|
return it->second;
|
}
|
|
CFont* font = new CFont();
|
LOGFONT logFont = { 0 };
|
_tcscpy_s(logFont.lfFaceName, _T("Segoe UI"));
|
logFont.lfHeight = -nFontSize;
|
logFont.lfQuality = CLEARTYPE_QUALITY;
|
font->CreateFontIndirect(&logFont);
|
m_mapFonts[nFontSize] = font;
|
|
return font;
|
}
|
|
void CIOMonitoringDlg::SetDefaultFont()
|
{
|
CFont* defaultFont = GetOrCreateFont(12);
|
|
// 遍历所有控件,应用默认字体
|
CWnd* pWnd = GetWindow(GW_CHILD);
|
while (pWnd) {
|
pWnd->SetFont(defaultFont, TRUE);
|
pWnd = pWnd->GetNextWindow();
|
}
|
}
|
|
void CIOMonitoringDlg::AdjustControls(float dScaleX, float dScaleY)
|
{
|
CWnd* pWnd = GetWindow(GW_CHILD);
|
while (pWnd) {
|
int nCtrlID = pWnd->GetDlgCtrlID();
|
if (nCtrlID != -1 && m_mapCtrlLayouts.find(nCtrlID) != m_mapCtrlLayouts.end()) {
|
CRect originalRect = m_mapCtrlLayouts[nCtrlID];
|
CRect newRect(
|
static_cast<int>(originalRect.left * dScaleX),
|
static_cast<int>(originalRect.top * dScaleY),
|
static_cast<int>(originalRect.right * dScaleX),
|
static_cast<int>(originalRect.bottom * dScaleY));
|
|
pWnd->MoveWindow(&newRect);
|
AdjustControlFont(pWnd, newRect.Width(), newRect.Height());
|
}
|
pWnd = pWnd->GetNextWindow();
|
}
|
}
|
|
void CIOMonitoringDlg::AdjustControlFont(CWnd* pWnd, int nWidth, int nHeight)
|
{
|
TCHAR szClassName[256];
|
GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName));
|
|
// 根据控件高度动态调整字体大小
|
int fontSize = nHeight / 2;
|
if (fontSize < 8) fontSize = 8;
|
if (fontSize < 24) fontSize = 24;
|
|
// 获取或创建字体
|
CFont* pFont = GetOrCreateFont(fontSize);
|
|
pWnd->SetFont(pFont);
|
pWnd->Invalidate(); // 刷新控件显示
|
}
|
|
void CIOMonitoringDlg::UpdatePageInfo()
|
{
|
// 格式化页码信息为 "当前页/总页数"
|
CString pageInfo;
|
pageInfo.Format(_T("%d/%d 页"), m_nCurrentPage, m_nTotalPages);
|
m_staticPageNum.SetWindowText(pageInfo);
|
}
|
|
void CIOMonitoringDlg::CreateDynamicControls()
|
{
|
CRect rect;
|
GetClientRect(&rect);
|
|
// 获取按钮的大小
|
CWnd* pPrevButton = GetDlgItem(IDC_BUTTON_PREV_PAGE);
|
CWnd* pNextButton = GetDlgItem(IDC_BUTTON_NEXT_PAGE);
|
|
CRect prevButtonRect, nextButtonRect;
|
pPrevButton->GetWindowRect(&prevButtonRect);
|
pNextButton->GetWindowRect(&nextButtonRect);
|
|
// 转换按钮坐标到对话框的坐标系统
|
ScreenToClient(&prevButtonRect);
|
ScreenToClient(&nextButtonRect);
|
|
int buttonHeight = prevButtonRect.Height(); // 按钮的高度
|
int topMargin = rect.Height() * 0.05; // 顶部保留 5% 的高度
|
int bottomMargin = buttonHeight + topMargin; // 底部保留按钮高度加间距
|
int sideMargin = topMargin; // 左右间距与顶部间距相同
|
int groupSpacing = 20; // 两组之间的间距
|
int verticalSpacing = 10; // 垂直间距
|
|
// 每行高度和列宽度
|
int availableHeight = rect.Height() - topMargin - bottomMargin;
|
int rowHeight = (availableHeight / m_nRowsPerPage) - verticalSpacing; // 控件高度包含间距
|
|
int availableWidth = rect.Width() - 2 * sideMargin; // 可用宽度(减去左右间距)
|
int colWidthSmall = availableWidth / 14; // 小宽度列更小
|
int colWidthLarge = availableWidth * 4 / 14; // 大宽度列比例
|
int groupWidth = colWidthSmall * 2 + colWidthLarge; // 每组总宽度
|
|
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);
|
x += colWidthSmall;
|
CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("X1000"), false, AlignCenter);
|
x += colWidthSmall;
|
CreateStaticControl(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]() {
|
// 自定义点击事件的逻辑
|
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);
|
}
|
});
|
}
|
});
|
x += colWidthSmall;
|
CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("Y1010"), false, AlignCenter);
|
x += colWidthSmall;
|
CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false);
|
}
|
}
|
|
void CIOMonitoringDlg::CreateStaticControl(int x, int y, int width, int height, const CString& text, bool hasBorder, TextAlign alignment, std::function<void()> clickCallback)
|
{
|
// 创建动态控件
|
CBLLabel* pStatic = new CBLLabel();
|
DWORD style = WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE; // 确保垂直居中
|
if (hasBorder) {
|
style |= WS_BORDER; // 添加边框
|
}
|
pStatic->Create(text, style, CRect(x, y, x + width, y + height), this);
|
|
// 设置文本对齐方式
|
pStatic->SetAlignment(alignment);
|
|
// 设置动态字体调整,并设置字体大小(动态字体会根据控件大小调整)
|
int nSize = height / 3;
|
CFont* pFont = GetOrCreateFont(nSize);
|
pStatic->SetFont(pFont);
|
pStatic->SetFontSize(nSize);
|
pStatic->SetDynamicFont(TRUE);
|
|
// 设置回调
|
if (clickCallback) {
|
pStatic->SetClickCallback(clickCallback);
|
}
|
|
// 存储控件指针
|
m_staticControls.push_back(pStatic);
|
}
|
|
void CIOMonitoringDlg::DisplayCurrentPage()
|
{
|
int startIndex = (m_nCurrentPage - 1) * m_nRowsPerPage;
|
int endIndex = min(startIndex + m_nRowsPerPage, static_cast<int>(m_displayData.size()));
|
|
m_inputPLCAddresses.clear();
|
m_outputPLCAddresses.clear();
|
|
for (int i = 0; i < m_nRowsPerPage; ++i) {
|
int row = i;
|
|
if (startIndex + i < endIndex) {
|
const auto& data = m_displayData[startIndex + i];
|
|
// 添加 PLC 地址到容器中
|
m_inputPLCAddresses.push_back(CString(data.inputAddress.c_str())); // 1 列
|
m_outputPLCAddresses.push_back(CString(data.outputAddress.c_str())); // 4 列
|
|
// 显示控件并设置内容
|
m_staticControls[row * m_nCols + 0]->SetWindowText(_T("OFF"));
|
m_staticControls[row * m_nCols + 0]->ShowWindow(SW_SHOW);
|
m_staticControls[row * m_nCols + 0]->SetBkColor(RGB(255, 0, 0));
|
|
m_staticControls[row * m_nCols + 1]->SetWindowText(CString(data.inputAddress.c_str()));
|
m_staticControls[row * m_nCols + 1]->ShowWindow(SW_SHOW);
|
|
m_staticControls[row * m_nCols + 2]->SetWindowText(CString(data.inputDescription.c_str()));
|
m_staticControls[row * m_nCols + 2]->ShowWindow(SW_SHOW);
|
|
m_staticControls[row * m_nCols + 3]->SetWindowText(_T("OFF"));
|
m_staticControls[row * m_nCols + 3]->ShowWindow(SW_SHOW);
|
m_staticControls[row * m_nCols + 3]->SetBkColor(RGB(255, 0, 0));
|
|
m_staticControls[row * m_nCols + 4]->SetWindowText(CString(data.outputAddress.c_str()));
|
m_staticControls[row * m_nCols + 4]->ShowWindow(SW_SHOW);
|
|
m_staticControls[row * m_nCols + 5]->SetWindowText(CString(data.outputDescription.c_str()));
|
m_staticControls[row * m_nCols + 5]->ShowWindow(SW_SHOW);
|
}
|
else {
|
// 隐藏这一行的所有控件
|
for (int col = 0; col < m_nCols; ++col) {
|
m_staticControls[row * m_nCols + col]->ShowWindow(SW_HIDE);
|
}
|
}
|
}
|
|
UpdatePageInfo();
|
}
|
|
void CIOMonitoringDlg::ClearDynamicControls()
|
{
|
for (auto* pStatic : m_staticControls) {
|
if (pStatic) {
|
pStatic->DestroyWindow();
|
delete pStatic;
|
}
|
}
|
m_staticControls.clear();
|
}
|
|
bool CIOMonitoringDlg::ParsePLCAddress(const CString& address, MC::SOFT_COMPONENT& component, int& addr)
|
{
|
if (address.GetLength() < 2) {
|
return false;
|
}
|
|
// 提取组件类型(第一个字符)
|
TCHAR componentChar = address[0];
|
switch (componentChar) {
|
case 'D':
|
component = MC::SOFT_COMPONENT::D;
|
break;
|
case 'M':
|
component = MC::SOFT_COMPONENT::M;
|
break;
|
case 'X':
|
component = MC::SOFT_COMPONENT::X;
|
break;
|
case 'Y':
|
component = MC::SOFT_COMPONENT::Y;
|
break;
|
default:
|
return false;
|
}
|
|
CString hexAddress = address.Mid(1);
|
addr = _tcstoul(hexAddress, nullptr, 16);
|
|
return true;
|
}
|
|
void CIOMonitoringDlg::UpdatePLCStates()
|
{
|
// 示例:从 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);
|
}
|
}
|
|
// 输出地址的读取
|
if (!m_outputPLCAddresses.empty()) {
|
// 获取起始地址和长度
|
CString startAddressStr = m_outputPLCAddresses.front();
|
CString endAddressStr = m_outputPLCAddresses.back();
|
MC::SOFT_COMPONENT component;
|
int startAddress, endAddress;
|
|
// 解析起始和结束地址
|
if (ParsePLCAddress(startAddressStr, component, startAddress) &&
|
ParsePLCAddress(endAddressStr, component, endAddress)) {
|
int outputSize = endAddress - startAddress + 1;
|
|
// 回调处理输出数据
|
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]);
|
|
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(component, startAddress, outputSize, funOnReadOutput);
|
}
|
}
|
}
|
|
BEGIN_MESSAGE_MAP(CIOMonitoringDlg, CDialogEx)
|
ON_BN_CLICKED(IDC_BUTTON_PREV_PAGE, &CIOMonitoringDlg::OnBnClickedButtonPrevPage)
|
ON_BN_CLICKED(IDC_BUTTON_NEXT_PAGE, &CIOMonitoringDlg::OnBnClickedButtonNextPage)
|
ON_WM_SIZE()
|
ON_WM_TIMER()
|
ON_WM_CLOSE()
|
END_MESSAGE_MAP()
|
|
|
// CIOMonitoringDlg 消息处理程序
|
|
|
BOOL CIOMonitoringDlg::OnInitDialog()
|
{
|
CDialogEx::OnInitDialog();
|
|
// TODO: 在此添加额外的初始化
|
CRect screenRect, dlgRect, clientRect;
|
SystemParametersInfo(SPI_GETWORKAREA, 0, &screenRect, 0);
|
|
GetClientRect(&clientRect);
|
m_nInitialWidth = clientRect.Width();
|
m_nInitialHeight = clientRect.Height();
|
|
// 初始化默认字体
|
CFont* pDefaultFont = GetOrCreateFont(12);
|
|
// 遍历所有子控件,记录初始位置并设置默认字体
|
CWnd* pWnd = GetWindow(GW_CHILD);
|
while (pWnd) {
|
int nCtrlID = pWnd->GetDlgCtrlID();
|
if (nCtrlID != -1) {
|
// 记录控件初始布局
|
CRect ctrlRect;
|
pWnd->GetWindowRect(&ctrlRect);
|
ScreenToClient(&ctrlRect);
|
m_mapCtrlLayouts[nCtrlID] = ctrlRect;
|
|
// 跳过特殊控件(如 MFCGridCtrl)
|
TCHAR szClassName[256];
|
GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName));
|
if (_tcsicmp(szClassName, _T("MFCGridCtrl")) == 0) {
|
pWnd = pWnd->GetNextWindow();
|
continue;
|
}
|
|
// 设置默认字体
|
pWnd->SetFont(pDefaultFont);
|
}
|
pWnd = pWnd->GetNextWindow();
|
}
|
|
GetWindowRect(&dlgRect);
|
int dlgWidth = dlgRect.Width() * 2;
|
int dlgHeight = dlgRect.Height() * 2;
|
|
if (dlgWidth > screenRect.Width()) {
|
dlgWidth = screenRect.Width();
|
}
|
if (dlgHeight > screenRect.Height()) {
|
dlgHeight = screenRect.Height();
|
}
|
|
int centerX = screenRect.left + (screenRect.Width() - dlgWidth) / 2;
|
int centerY = screenRect.top + (screenRect.Height() - dlgHeight) / 2;
|
MoveWindow(centerX, centerY, dlgWidth, dlgHeight);
|
|
CreateDynamicControls();
|
DisplayCurrentPage();
|
|
SetTimer(TIMER_READ_PLC_DATA, 500, nullptr);
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
// 异常: OCX 属性页应返回 FALSE
|
}
|
|
void CIOMonitoringDlg::OnSize(UINT nType, int cx, int cy)
|
{
|
CDialogEx::OnSize(nType, cx, cy);
|
|
// TODO: 在此处添加消息处理程序代码
|
if (nType == SIZE_MINIMIZED || m_mapCtrlLayouts.empty()) {
|
return;
|
}
|
|
float dScaleX = static_cast<float>(cx) / m_nInitialWidth;
|
float dScaleY = static_cast<float>(cy) / m_nInitialHeight;
|
|
// 遍历对话框中的所有控件
|
AdjustControls(dScaleX, dScaleY);
|
}
|
|
void CIOMonitoringDlg::OnBnClickedButtonPrevPage()
|
{
|
// TODO: 在此添加控件通知处理程序代码
|
if (m_nCurrentPage > 1) {
|
--m_nCurrentPage;
|
DisplayCurrentPage();
|
}
|
else {
|
AfxMessageBox(_T("已经是第一页!"));
|
}
|
}
|
|
void CIOMonitoringDlg::OnBnClickedButtonNextPage()
|
{
|
// TODO: 在此添加控件通知处理程序代码
|
if (m_nCurrentPage < m_nTotalPages) {
|
++m_nCurrentPage;
|
DisplayCurrentPage();
|
}
|
else {
|
AfxMessageBox(_T("已经是最后一页!"));
|
}
|
}
|
|
void CIOMonitoringDlg::OnTimer(UINT_PTR nIDEvent)
|
{
|
// TODO: 在此添加消息处理程序代码和/或调用默认值
|
if (TIMER_READ_PLC_DATA == nIDEvent) {
|
ASSERT(m_pPLC);
|
UpdatePLCStates();
|
Sleep(100);
|
}
|
CDialogEx::OnTimer(nIDEvent);
|
}
|
|
void CIOMonitoringDlg::OnClose()
|
{
|
// TODO: 在此添加消息处理程序代码和/或调用默认值
|
KillTimer(TIMER_READ_PLC_DATA);
|
CDialogEx::OnClose();
|
}
|