TSI_Config/tmrrelayassociation.cpp
2025-04-27 15:23:05 +08:00

558 lines
22 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "tmrrelayassociation.h"
#include "ui_tmrrelayassociation.h"
#include "vibrationdata.h"
#include <QStack>
#include <QMenu>
#include <QMessageBox>
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>(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<int>::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<QAbstractButton *> buttonList = btnGroup_slot->buttons();
for (int i = 1; i < buttonList.count() + 1; i++) {
std::shared_ptr<CardBase> 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<CardBase> base_ptr = ConfigMgr::Instance()->GetSlotPtr(slot_no);
if (base_ptr == nullptr) {
// do nothing or use template to init it.
relay_data = std::make_shared<TmrrelayassociationData>();
relay_data->card_type_ = car_type;
relay_data->slot_ = slot_no;
ConfigMgr::Instance()->AddCard(relay_data);
std::shared_ptr<TmrrelayassociationData> relay_data_backup = std::make_shared<TmrrelayassociationData>();
relay_data_backup->card_type_ = kCardRelayTMRBackup;
relay_data_backup->slot_ = slot_no + 1;
ConfigMgr::Instance()->AddCard(relay_data_backup);
std::shared_ptr<TmrrelayassociationData> relay_data_backup2 = std::make_shared<TmrrelayassociationData>();
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<TmrrelayassociationData>(base_ptr);
qDebug() << "logic" <<relay_data->tmr_relay[current_index].logic_expression;
for(int i = 0 ; i < SLOT_NUM ; i++){
std::shared_ptr<CardBase> cardbase_ptr = ConfigMgr::Instance()->GetSlotPtr(i + 1);
if(cardbase_ptr != nullptr &&
cardbase_ptr->card_type_ == kCardVibSingle){
qDebug() << "i" << i;
std::shared_ptr<VibrationData> ptr = std::dynamic_pointer_cast<VibrationData>(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<ExprNode*> nodeStack;
QStack<QString> 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" <<displayText << node->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<QStandardItem*> stack; // 用来存储节点
QStack<QString> 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<CardBase> base_ptr = ConfigMgr::Instance()->GetSlotPtr(button_id);
std::shared_ptr<VibrationData> ptr = std::dynamic_pointer_cast<VibrationData>(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<CardBase> 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<QStandardItemModel *>(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<QStandardItemModel *>(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" <<endl;
QModelIndex curIndex = treeView_relay->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<QStandardItemModel *>(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);
}