在我工作的第一年,IO诊断模块是我接触到的首个较为复杂的任务。该模块主要用于检测和诊断系统中的输入、输出及模拟量信号,确保系统正常运行并及时发现潜在故障。作为系统首要的测试功能,在实际测试阶段确实暴露了大量问题,这使它成为了一个非常适合新人锻炼的入门项目。
首先功能需求为:
a.输入诊断(Digital Input Diagnostic):
- 确保输入信号从传感器或其他设备正确传递到控制器。
- 检测输入信号的电压、电流是否符合预期范围。
- 确认输入信号在不同条件下的响应和稳定性。
b.输出诊断(Digital Output Diagnostic):
- 确保控制器发送给执行器或其他设备的输出信号正确且稳定。
- 检测输出信号的电压、电流是否在设定值范围内。
- 验证输出信号的动作和响应是否按预期执行。
c.模拟量诊断(Analog Input/Output Diagnostic):
- 检测模拟输入信号(如传感器输出的模拟电压或电流)是否在预期的测量范围内。
- 确认模拟输出信号(如控制器输出的模拟电压或电流)是否在设定的范围内,并且稳定。
系统采用双模块设计:诊断模块和硬件配置模块。这两个模块分别对应物理点和逻辑点。其中,物理点代表硬件连接的实际点位,而逻辑点则可以根据需求灵活配置。由于硬件限制,物理点数量通常少于逻辑点数量。为实现这一功能,首要步骤是完成点位分配功能,允许用户将任意逻辑点自由映射到物理点上。
1.点位分配
根据诊断界面和硬件配置自动匹配相应功能。硬件配置包含所有支持的逻辑点,只需在输入框填写索引值,即可将对应点位显示在诊断页面,实现灵活的点位分配功能。
例如:将某逻辑点索引设为n后,物理点n就会接收该逻辑点对应的数据。通过建立两张映射表(逻辑点表和物理点表)的对应关系,即可实现该功能。
# 理论上说应该是:两张表 logicc[n1] pyhsic[n2]
# 转换公式为:
if logic[n] = t
pyhsic[t-1] = n+1
t-1是考虑逻辑点从0开始,n+1考虑物理点从1开始
诊断页面的文本控件会根据pyhsic[]数组的值动态显示对应文本内容。这些文本数据从外部文件加载,每个物理点的显示内容都与指定的数组元素相绑定。
2.覆盖 交换 功能
当设置两个点位为相同值时,若同时显示同一设备接入,通常并不合理。此时应根据实际使用需求来定制功能。
首先实现这个弹窗显示,在edit接收到值的时候判断:
setPopup(char_t *editText){
//弹窗弹出条件
//判断此值跟目前存在的值是否相同
int32_t judge = ieditPopup(iotest->iotest,iotest->para,current_value);
//qDebug()<<"void wgt_itemedit::setPopup judge = "<<judge;
if(iotest->iotest[0] != '\0' && judge == 0){
QTextCodec *codec = QTextCodec::codecForName("GBK");
wgt_keyboardbackground *test = new wgt_keyboardbackground;
test->show();
//创建空白弹窗
HtPopup *window = new HtPopup(gui_get_main_gui());
QIcon releaseImg = QIcon(QPixmap("./pic/keyboard_normal.bmp").scaled(80,35));
QIcon pressImg = QIcon(QPixmap("./pic/keyboard_normal_active.bmp").scaled(80,35));
QString warmingpicpath("./pic/question_mark.PNG");
QString showtext(codec->toUnicode("此值已存在,是否替换或直接覆盖?"));
//按钮--替换
HtDialogButton *changeBtn = new HtDialogButton(window);
changeBtn->setGeometry(100,215,150,70);
changeBtn->setText(codec->toUnicode("替换"));
changeBtn->setStyleSheet("font-size:17px;");
changeBtn->setIcons(0, releaseImg);
changeBtn->setIcons(1, pressImg);
changeBtn->selectIcon(0);
QObject::connect(changeBtn, SIGNAL(pressed()), changeBtn, SLOT(onIconChang()));
QObject::connect(changeBtn, SIGNAL(released()), changeBtn, SLOT(onIconChang()));
QObject::connect(changeBtn, &HtDialogButton::clicked, [=]() {
changeValue(iotest->para);
window->close();
test->close();
});
//按钮--覆盖
HtDialogButton *resetBtn = new HtDialogButton(window);
resetBtn->setGeometry(300,215,150,70);
resetBtn->setText(codec->toUnicode("覆盖"));
resetBtn->setStyleSheet("font-size:17px;");
resetBtn->setIcons(0, releaseImg);
resetBtn->setIcons(1, pressImg);
resetBtn->selectIcon(0);
QObject::connect(resetBtn, SIGNAL(pressed()), resetBtn, SLOT(onIconChang()));
QObject::connect(resetBtn, SIGNAL(released()), resetBtn, SLOT(onIconChang()));
QObject::connect(resetBtn, &HtDialogButton::clicked, [=]() {
coverValue(iotest->para);
window->close();
test->close();
});
//按钮--取消
HtDialogButton *concelBtn = new HtDialogButton(window);
concelBtn->setGeometry(500,215,150,70);
concelBtn->setText(codec->toUnicode("取消"));
concelBtn->setStyleSheet("font-size:17px;");
concelBtn->setIcons(0, releaseImg);
concelBtn->setIcons(1, pressImg);
concelBtn->selectIcon(0);
QObject::connect(concelBtn, SIGNAL(pressed()), concelBtn, SLOT(onIconChang()));
QObject::connect(concelBtn, SIGNAL(released()), concelBtn, SLOT(onIconChang()));
QObject::connect(concelBtn, &HtDialogButton::clicked, [=]() {
concelValue(iotest->para);
window->close();
test->close();
});
//按钮-关闭
HtButton *closeBtn = new HtButton(window);
closeBtn->setFixedSize(BTN_SIZE,BTN_SIZE);
closeBtn->move( window->width() - BTN_SIZE , 0 );
QObject::connect(closeBtn, &HtButton::clicked, [=]() {
concelValue(iotest->para);
window->close();
test->close();
});
// 创建警示label
HtLabel *warmingLabel = new HtLabel(window);
warmingLabel->LoadPicturePar(warmingpicpath,120,120);
warmingLabel->move(60,80);
warmingLabel->InitForm();
// 创建内容label
HtLabel *showlabel = new HtLabel(window);
showlabel->setText(showtext);
showlabel->setStyleSheet("font-size:20px;");
showlabel->setGeometry(200,117,430,50);
showlabel->setAlignment(Qt::AlignCenter);
window->PopupShow();
}
}
3.强制功能
强制功能主要涉及位运算处理。系统接收一个8位二进制值,每位0/1代表不同状态。需要设计一个按钮来解析该值并根据需求进行修改。
// 修改idata的第bits位
if (ptrcldata->selectedData) {
idata |= (1 << ptrcldata->bits); // 将第bits位置1
}else{
idata &= ~(1 << ptrcldata->bits); // 将第bits位清零
}
// 切换按钮状态
ptrcldata->selectedData = !ptrcldata->selectedData;
selectBackground(ptrcldata->selectedData);
整个流程如上所述,中间交换的具体逻辑将在下篇文章中详细说明。