#include "tmrrelayassociation.h" #include "ui_tmrrelayassociation.h" #include "vibrationdata.h" #include #include #include TMRRelayAssociation::TMRRelayAssociation(int slot,int cardtype,QWidget *parent) : QDialog(parent) , ui(new Ui::TMRRelayAssociation) { ui->setupUi(this); slot_no = slot; car_type = static_cast(cardtype); if(car_type == kCardRelaySingle){ ui->checkBox_sgcc->setVisible(0); } ui->label_slot_no->setText(QString::number(slot_no)); QVBoxLayout *layout_available = new QVBoxLayout(ui->widget_available); list_widget_available = new QListWidget(); layout_available->addWidget(list_widget_available); list_widget_available->setDragEnabled(true); QVBoxLayout *layout_relay = new QVBoxLayout(ui->widget_relay); treeView_relay = new QTreeView; layout_relay->addWidget(treeView_relay); treeView_relay->setDragEnabled(true); // 启用拖动 treeView_relay->setAcceptDrops(true); // 接受放下 treeView_relay->setDropIndicatorShown(true); // 显示放置指示器 treeView_relay->setDragDropMode(QAbstractItemView::DropOnly); model_Relay = new DropTreeModel(this); //创建模型指定父类 treeView_relay->setModel(model_Relay); treeView_relay->setHeaderHidden(true); btnGroup_slot = new QButtonGroup(this); btnGroup_slot->addButton(ui->pushButton_slot1); btnGroup_slot->addButton(ui->pushButton_slot2); btnGroup_slot->addButton(ui->pushButton_slot3); btnGroup_slot->addButton(ui->pushButton_slot4); btnGroup_slot->addButton(ui->pushButton_slot5); btnGroup_slot->addButton(ui->pushButton_slot6); btnGroup_slot->addButton(ui->pushButton_slot7); btnGroup_slot->addButton(ui->pushButton_slot8); btnGroup_slot->addButton(ui->pushButton_slot9); btnGroup_slot->addButton(ui->pushButton_slot10); btnGroup_slot->addButton(ui->pushButton_slot11); btnGroup_slot->addButton(ui->pushButton_slot12); btnGroup_slot->addButton(ui->pushButton_slot13); btnGroup_slot->addButton(ui->pushButton_slot14); btnGroup_slot->addButton(ui->pushButton_slot15); btnGroup_slot->addButton(ui->pushButton_slot16); connect(btnGroup_slot, SIGNAL(buttonClicked(QAbstractButton *)), this, SLOT(OnButtonGroup(QAbstractButton *))); connect(ui->comboBox_relay_ch, QOverload::of(&QComboBox::currentIndexChanged), this, &TMRRelayAssociation::onComboBoxIndexChanged); treeView_relay->setContextMenuPolicy(Qt::CustomContextMenu); connect(treeView_relay,&QTreeView::customContextMenuRequested,this,&TMRRelayAssociation::on_treeView_Relay_customContextMenuRequested); current_index = ui->comboBox_relay_ch->currentIndex(); Init(); onComboBoxIndexChanged(current_index); // QString expr = "((S01C01A1 + S01C02A1 + (S02C01A1 * S02C01A2)) * (S02C01A2 + S02C01A1 + (S02C01A1 + S02C01A2)) * (S02C01A1 * S02C01A2))"; // setExpressionToTreeView(treeView_relay, expr); } TMRRelayAssociation::~TMRRelayAssociation() { delete ui; } void TMRRelayAssociation::Init(){ QList buttonList = btnGroup_slot->buttons(); for (int i = 1; i < buttonList.count() + 1; i++) { std::shared_ptr base_ptr = ConfigMgr::Instance()->GetSlotPtr(i); if(base_ptr != nullptr){ switch (base_ptr->card_type_) { case kCardVibSingle :{ buttonList[i - 1]->setText("振动"); break; } case kCardKeyphaseSingle:{ buttonList[i - 1]->setText("键相"); break; } case kCardSpeedSingle:{ buttonList[i - 1]->setText("转速"); break; } case kCardRelaySingle: case kCardRelaySingleNOK: case kCardRelayTMRPrimary:{ buttonList[i - 1]->setText("继电器"); break; } default: break; } } } std::shared_ptr base_ptr = ConfigMgr::Instance()->GetSlotPtr(slot_no); if (base_ptr == nullptr) { // do nothing or use template to init it. relay_data = std::make_shared(); relay_data->card_type_ = car_type; relay_data->slot_ = slot_no; ConfigMgr::Instance()->AddCard(relay_data); std::shared_ptr relay_data_backup = std::make_shared(); relay_data_backup->card_type_ = kCardRelayTMRBackup; relay_data_backup->slot_ = slot_no + 1; ConfigMgr::Instance()->AddCard(relay_data_backup); std::shared_ptr relay_data_backup2 = std::make_shared(); relay_data_backup2->card_type_ = kCardRelayTMRBackup; relay_data_backup2->slot_ = slot_no + 2; ConfigMgr::Instance()->AddCard(relay_data_backup2); return; } relay_data = std::dynamic_pointer_cast(base_ptr); qDebug() << "logic" <tmr_relay[current_index].logic_expression; for(int i = 0 ; i < SLOT_NUM ; i++){ std::shared_ptr cardbase_ptr = ConfigMgr::Instance()->GetSlotPtr(i + 1); if(cardbase_ptr != nullptr && cardbase_ptr->card_type_ == kCardVibSingle){ qDebug() << "i" << i; std::shared_ptr ptr = std::dynamic_pointer_cast(cardbase_ptr); for (int var = 0; var < CHANNEL_COUNT; ++var) { QString item_data,item_str; if(ptr->alert_danger[var].direct_enable || ptr->alert_danger[var].x1_ampl_enable || ptr->alert_danger[var].x2_ampl_enable){ item_str = QString("%1 (槽位 %2 通道 %3 警报)").arg(ptr->base_config_[var].point_name).arg(ptr->base_config_[var].chan_id.mid(1,2)).arg(ptr->base_config_[var].chan_id.mid(4,2)); item_data = QString("%1A1").arg(ptr->base_config_[var].chan_id); } channelNameMap[item_data] = item_str; if(ptr->alert_danger[var].danger_enable){ item_str = QString("%1 (槽位 %2 通道 %3 危险)").arg(ptr->base_config_[var].point_name).arg(ptr->base_config_[var].chan_id.mid(1,2)).arg(ptr->base_config_[var].chan_id.mid(4,2)); item_data = QString("%1A2").arg(ptr->base_config_[var].chan_id); } channelNameMap[item_data] = item_str; } }else if(cardbase_ptr != nullptr && cardbase_ptr->card_type_ == kCardSpeedSingle){ } } ui->checkBox_sgcc->setChecked(relay_data->sgcc_enable); if(!relay_data->tmr_relay[current_index].logic_expression.isEmpty()){ setExpressionToTreeView(treeView_relay, relay_data->tmr_relay[current_index].logic_expression); } } ExprNode* TMRRelayAssociation::parseExpression(const QString& expr, int& pos) { auto skipSpaces = [&]() { while (pos < expr.length() && expr[pos].isSpace()) pos++; }; QStack nodeStack; QStack opStack; while (pos < expr.length()) { skipSpaces(); if (pos >= expr.length()) break; QChar ch = expr[pos]; if (ch == '(') { pos++; nodeStack.push(parseExpression(expr, pos)); } else if (ch == ')') { pos++; break; } else if (ch == '+' || ch == '*') { opStack.push(QString(ch)); pos++; } else { QString token; while (pos < expr.length() && expr[pos].isLetterOrNumber()) { token += expr[pos++]; } nodeStack.push(new ExprNode{token, {}}); } while (opStack.size() > 0 && nodeStack.size() >= 2) { QString op = opStack.pop(); ExprNode* right = nodeStack.pop(); ExprNode* left = nodeStack.pop(); if (left->value == op) { left->children.append(right); nodeStack.push(left); } else if (right->value == op) { right->children.prepend(left); nodeStack.push(right); } else { ExprNode* parent = new ExprNode{op, {left, right}}; nodeStack.push(parent); } } } return nodeStack.isEmpty() ? nullptr : nodeStack.top(); } QStandardItem* TMRRelayAssociation::buildItemTree(ExprNode* node) { if (!node) return nullptr; QString displayText; if (node->value == "+" || node->value == "*") { displayText = (node->value == "+") ? "OR" : "AND"; // 运算符显示 } else { displayText = channelNameMap.value(node->value, node->value); // 显示名 qDebug() << "display" <value; } QStandardItem* item = new QStandardItem(displayText); item->setData(node->value, Qt::UserRole); // 原始表达式key for (ExprNode* child : node->children) { item->appendRow(buildItemTree(child)); } return item; } void TMRRelayAssociation::setExpressionToTreeView(QTreeView* treeView, const QString& expr) { int pos = 0; model_Relay->clear(); ExprNode* root = parseExpression(expr, pos); QStandardItem* rootItem = buildItemTree(root); model_Relay->appendRow(rootItem); treeView_relay->expandAll(); } QString TMRRelayAssociation::buildLogicExpression(QStandardItem *item) { if (!item) return ""; int childCount = item->rowCount(); QVariant userData = item->data(Qt::UserRole); QString text = userData.toString().trimmed(); if (childCount == 0) { // 叶子节点,直接返回表达式,比如 S01C01A1 return text; } // 判断当前是 +(OR)还是 *(AND) QString opStr = (text == "+") ? "+" : (text == "*") ? "*" : ""; // 如果不是 +/*,视为叶子节点 if (opStr.isEmpty()) { return text; } QStringList subExprs; for (int i = 0; i < childCount; ++i) { QStandardItem *child = item->child(i); subExprs << buildLogicExpression(child); } return "(" + subExprs.join(" " + opStr + " ") + ")"; } QStandardItem* TMRRelayAssociation::parseExpression(const QString &expr) { QStack stack; // 用来存储节点 QStack ops; // 用来存储操作符 int pos = 0; int length = expr.length(); while (pos < length) { QRegExp regex("(\\()|([A-Za-z0-9]+)|([+*])|(\\))"); if (regex.indexIn(expr, pos) != -1) { QString matchedText = regex.cap(0); QString group1 = regex.cap(1); // ( QString group2 = regex.cap(2); // S01C01A1 QString group3 = regex.cap(3); // + or * QString group4 = regex.cap(4); // ) // 处理左括号,表示子表达式的开始 if (!group1.isEmpty()) { ops.push("("); // 推入栈 } // 处理右括号,表示子表达式的结束 else if (!group4.isEmpty()) { // 遇到右括号时开始弹出栈中的操作符,直到遇到左括号 while (!ops.isEmpty() && ops.top() != "(") { QString op = ops.pop(); QStandardItem* right = stack.pop(); QStandardItem* left = stack.pop(); QStandardItem* node = new QStandardItem(op); node->appendRow(left); // 左子树 node->appendRow(right); // 右子树 stack.push(node); // 将新的节点推入栈 } ops.pop(); // 弹出左括号 } // 处理操作符 + 和 * else if (!group3.isEmpty()) { ops.push(group3); // 操作符入栈 } // 处理终端元素(比如 S01C01A1) else if (!group2.isEmpty()) { QStandardItem* item = new QStandardItem(group2); stack.push(item); // 将数据节点推入栈 } pos = regex.pos(0) + regex.matchedLength(); } else { break; // 防止无限循环 } } // 最终合并栈中的所有内容 while (!ops.isEmpty()) { QString op = ops.pop(); QStandardItem* right = stack.pop(); QStandardItem* left = stack.pop(); QStandardItem* node = new QStandardItem(op); node->appendRow(left); // 左子树 node->appendRow(right); // 右子树 stack.push(node); } // 返回栈顶的节点,应该是根节点 return stack.isEmpty() ? nullptr : stack.pop(); } ExprValidationResult TMRRelayAssociation::validateLogicExpression(const QString& expr) { int bracketCount = 0; bool lastWasOperator = true; bool lastWasOpenParen = false; for (int i = 0; i < expr.length(); ++i) { QChar ch = expr[i]; if (ch.isSpace()) continue; if (ch == '(') { bracketCount++; lastWasOpenParen = true; lastWasOperator = true; } else if (ch == ')') { bracketCount--; if (bracketCount < 0) { return {false, i, "多余的右括号 ')'"}; } lastWasOpenParen = false; lastWasOperator = false; } else if (ch == '+' || ch == '*') { if (lastWasOperator || lastWasOpenParen) { return {false, i, QString("无效的运算符 '%1' 的位置").arg(ch)}; } lastWasOperator = true; lastWasOpenParen = false; } else if (ch.isLetterOrNumber()) { QString token; int start = i; while (i < expr.length() && expr[i].isLetterOrNumber()) { token += expr[i]; ++i; } --i; // 修正多读了一位 if (token.isEmpty()) { return {false, start, "变量名称缺失"}; } lastWasOperator = false; lastWasOpenParen = false; } else { return {false, i, QString("不支持的字符 '%1'").arg(ch)}; } } if (bracketCount != 0) { return {false, expr.length(), "括号不匹配"}; } if (lastWasOperator) { return {false, expr.length() - 1, "表达式不能以运算符结尾"}; } return {true, -1, ""}; } void TMRRelayAssociation::buildTreeFromExpression(QTreeView *treeView, const QString &expr) { QStandardItem* rootItem = parseExpression(expr); if (rootItem) { model_Relay->appendRow(rootItem); } treeView_relay->setModel(model_Relay); } void TMRRelayAssociation::OnButtonGroup(QAbstractButton *slot_btn) { if (slot_btn != NULL) { list_widget_available->clear(); QString object_name = slot_btn->objectName(); if(slot_btn->text().isEmpty()) return; qDebug() << object_name; int button_id = object_name.right(object_name.length() - 15).toInt(); std::shared_ptr base_ptr = ConfigMgr::Instance()->GetSlotPtr(button_id); std::shared_ptr ptr = std::dynamic_pointer_cast(base_ptr); QListWidgetItem *item_and = new QListWidgetItem("AND"); item_and->setData(Qt::UserRole, "*"); list_widget_available->addItem(item_and); QListWidgetItem *item_or = new QListWidgetItem("OR"); item_or->setData(Qt::UserRole, "+"); list_widget_available->addItem(item_or); for(int var = 0; var < CHANNEL_COUNT ; ++var){ std::shared_ptr base_ptr = ConfigMgr::Instance()->GetSlotPtr(button_id); if(base_ptr->card_type_ == kCardVibSingle){ QString item_data; if(ptr->base_config_[var].standby && (var % 2)) continue; if(ptr->alert_danger[var].direct_enable || ptr->alert_danger[var].x1_ampl_enable || ptr->alert_danger[var].x2_ampl_enable){ QString item_str = QString("%1 (槽位 %3 通道 %4 警报)").arg(ptr->base_config_[var].point_name).arg(QString::number(button_id, 10).rightJustified(2, '0')).arg(QString::number(var+1, 10).rightJustified(2, '0')); QListWidgetItem *item = new QListWidgetItem(item_str); item_data = QString("S%1C%2A1").arg(QString::number(button_id, 10).rightJustified(2, '0')).arg(QString::number(var+1, 10).rightJustified(2, '0')); item->setData(Qt::UserRole, item_data); list_widget_available->addItem(item); } if(ptr->alert_danger[var].danger_enable){ QString item_str = QString("%1 (槽位 %3 通道 %4 危险)").arg(ptr->base_config_[var].point_name).arg(QString::number(button_id, 10).rightJustified(2, '0')).arg(QString::number(var+1, 10).rightJustified(2, '0')); QListWidgetItem *item = new QListWidgetItem(item_str); item_data = QString("S%1C%2A2").arg(QString::number(button_id, 10).rightJustified(2, '0')).arg(QString::number(var+1, 10).rightJustified(2, '0')); item->setData(Qt::UserRole, item_data); list_widget_available->addItem(item); } } } } } void TMRRelayAssociation::on_pushButton_cancel_clicked() { this->close(); } void TMRRelayAssociation::on_pushButton_confirm_clicked() { QStandardItemModel *model = qobject_cast(treeView_relay->model()); if (!model) return; QStandardItem *root = model->invisibleRootItem(); QString finalExpr; for (int i = 0; i < root->rowCount(); ++i) { QStandardItem *topItem = root->child(i); QString expr = buildLogicExpression(topItem); finalExpr += expr; } ExprValidationResult result = validateLogicExpression(finalExpr); if (!result.isValid && !finalExpr.isEmpty()) { QMessageBox::warning(this, "表达式错误", QString("错误位置:%1\n错误描述:%2").arg(result.errorPos).arg(result.errorMsg)); return; } relay_data->tmr_relay[current_index].logic_expression = finalExpr; relay_data->sgcc_enable = ui->checkBox_sgcc->checkState(); qDebug() << "逻辑表达式:" << finalExpr; this->close(); } void TMRRelayAssociation::onComboBoxIndexChanged(int index){ QStandardItemModel *model = qobject_cast(treeView_relay->model()); if (!model) return; QStandardItem *root = model->invisibleRootItem(); QString finalExpr; for (int i = 0; i < root->rowCount(); ++i) { QStandardItem *topItem = root->child(i); QString expr = buildLogicExpression(topItem); finalExpr += expr; } ExprValidationResult result = validateLogicExpression(finalExpr); if (!result.isValid && !finalExpr.isEmpty()) { QMessageBox::warning(this, "表达式错误", QString("错误位置:%1\n错误描述:%2").arg(result.errorPos).arg(result.errorMsg)); return; } relay_data->tmr_relay[current_index].logic_expression = finalExpr; current_index = index; if(relay_data->tmr_relay[index].logic_expression != "") setExpressionToTreeView(treeView_relay, relay_data->tmr_relay[index].logic_expression); else model_Relay->clear(); } void TMRRelayAssociation::on_pushButton_and_clicked() { } void TMRRelayAssociation::on_pushButton_or_clicked() { } void TMRRelayAssociation::slotDeleteItem() { QModelIndex curIndex = treeView_relay->currentIndex(); if(curIndex.isValid()){ model_Relay->removeRow(curIndex.row(),curIndex.parent()); } } void TMRRelayAssociation::on_treeView_Relay_customContextMenuRequested(const QPoint &pos) { qDebug() << "on_treeView_Relay_customContextMenuRequested" <indexAt(pos); //当前点击的元素的index QModelIndex index = curIndex.sibling(curIndex.row(),0); //该行的第1列元素的index QMenu menu(this); if (index.isValid()){ //添加一行菜单,进行展开 menu.addAction(QStringLiteral("删除"), this, SLOT(slotDeleteItem())); menu.addSeparator(); //添加一个分隔线 } menu.exec(QCursor::pos()); //显示菜单 } void TMRRelayAssociation::on_checkBox_sgcc_stateChanged(int arg1) { if(ui->checkBox_sgcc->checkState()){ treeView_relay->setEnabled(false); list_widget_available->setEnabled(false); ui->comboBox_relay_ch->setEnabled(false); ui->pushButton_logic->setEnabled(false); ui->textEdit_logic->setEnabled(false); }else{ treeView_relay->setEnabled(true); list_widget_available->setEnabled(true); ui->comboBox_relay_ch->setEnabled(true); ui->pushButton_logic->setEnabled(true); ui->textEdit_logic->setEnabled(true); } } void TMRRelayAssociation::on_pushButton_logic_clicked() { QStandardItemModel *model = qobject_cast(treeView_relay->model()); if (!model) return; QStandardItem *root = model->invisibleRootItem(); QString finalExpr; for (int i = 0; i < root->rowCount(); ++i) { QStandardItem *topItem = root->child(i); QString expr = buildLogicExpression(topItem); finalExpr += expr; } ExprValidationResult result = validateLogicExpression(finalExpr); if (!result.isValid && !finalExpr.isEmpty()) { QMessageBox::warning(this, "表达式错误", QString("错误位置:%1\n错误描述:%2").arg(result.errorPos).arg(result.errorMsg)); return; } ui->textEdit_logic->setPlainText(finalExpr); }