/
pi_coder
/
ByteMachine
Обзор
Документация
Войти
/
pi_coder
/
ByteMachine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/nodes/dump_node.cpp
698 строк
24 KB
pimenov-and
Перенос функциональности из старого проекта, в основном это описания узлов
19 июл 2026, 20:13
19 июл 2026, 20:13
cb3c51b
Код
Авторство
О чём код?
//////////////////////////////////////////////////////////////// // ByteMachine // Узел для вывода дампа данных //////////////////////////////////////////////////////////////// #include "dump_node.h" #include "node_name_manager.h" #include "undo/undo_change_object_prop_value.h" #include "colors.h" #include "qt_helper.h" #include <QPainter> #include <QDomDocument> #include <QUndoStack> //============================================================== using std::size_t; //============================================================== // Прототипы функций //============================================================== // Конвертация набора байтов в хекс-строку QString bytesToHexLine(const ByteList &bytes); // Конвертация набора байтов в ASCII-строку QString bytesToAsciiLine(const ByteList &bytes); // Расчёт адреса данных по параметрам quint32 calcDataAddress(int lineIndex, ByteWidths byteWidth); // Конвертация адреса данных в строку QString dataAddressToHexStr(quint32 address); //////////////////////////////////////////////////////////////// // Реализация класса DumpNode //////////////////////////////////////////////////////////////// //============================================================== // Конструктор с параметрами //============================================================== DumpNode::DumpNode(QUndoStack *undoStack, QObject *parent) : BaseNode{undoStack, parent} { Q_ASSERT(undoStack != nullptr); name_ = nodeNameManager()->addName("dump"); Q_ASSERT(!name_.isEmpty()); createInputPin(); createOutputPin(); // Задание начальных размером const int width = DumpNode::minWidth(); const int height = DumpNode::minHeight(); setUndo(true); BaseNode::setWidth(width); BaseNode::setHeight(height); setUndo(false); DumpNode::updateStateInfo(); } //============================================================== // Чтение из XML //============================================================== void DumpNode::readFromXml(const QDomElement &elem) { Q_ASSERT(!elem.isNull()); const QString name = readNameFromXml(elem); const qint32 left = readLeftFromXml(elem); const qint32 top = readTopFromXml(elem); const qint32 width = readWidthFromXml(elem); const qint32 height = readHeightFromXml(elem); const bool isBypass = readBypassFromXml(elem); const QString comment = readCommentFromXml(elem); setName(name); setLeft(left); setTop(top); setWidth(width); setHeight(height); setBypass(isBypass); setComment(comment); } //============================================================== // Запись в XML //============================================================== void DumpNode::writeToXml(QDomDocument &doc, QDomElement &elem) const { Q_ASSERT(!doc.isNull()); Q_ASSERT(!elem.isNull()); writeIdToXml(doc, elem); writeNameToXml(doc, elem); writeLeftToXml(doc, elem); writeTopToXml(doc, elem); writeWidthToXml(doc, elem); writeHeightToXml(doc, elem); writeBypassToXml(doc, elem); writeCommentToXml(doc, elem); } //============================================================== // Получение текста подсказки //============================================================== QString DumpNode::tooltipText() const { QString text{}; text += QString{"%1: \"%2\"\n"}.arg(tr("Name"), name()); text += QString{"%1: %2\n"}.arg(tr("Mode"), dumpModeToStrTr(dumpMode_)); text += QString{"%1: %2\n"}.arg(tr("Width (B)")).arg(byteWidthToInt(byteWidth_)); text += QString{"%1: %2\n"}.arg(tr("Bypass"), boolToStrTr(isBypass())); text += QString{"%1: \"%2\""}.arg(tr("Comment"), comment()); return text; } //============================================================== // Получение размера данных //============================================================== size_t DumpNode::dataSize() const { if (stateInfo_.isError()) { return 0; } return inputDataSize(); } //============================================================== // Получение байта данных //============================================================== quint8 DumpNode::dataByte(size_t index) const { Q_ASSERT_X(index < dataSize(), "Check index", qPrintable(QString{"index: %1, dataSize: %2"}.arg(index).arg(dataSize()))); return inputPin_->dataByte(index); } //============================================================== // Получение блока данных //============================================================== ByteList DumpNode::dataBlock(size_t index, size_t count) const { return inputPin_->dataBlock(index, count); } //============================================================== // Функция вызывается при изменении данных //============================================================== void DumpNode::dataChanged() { startHighlightTimer(); updateStateInfo(); outputPin_->dataChanged(); } //============================================================== // Получение входных пинов //============================================================== QVector<ShPtrInputPin> DumpNode::inputPins() { return QVector<ShPtrInputPin>{inputPin_}; } //============================================================== // Получение входных пинов (2 вариант) //============================================================== QVector<ShPtrConstInputPin> DumpNode::inputPins() const { return QVector<ShPtrConstInputPin>{inputPin_}; } //============================================================== // Получение выходных пинов //============================================================== QVector<ShPtrOutputPin> DumpNode::outputPins() { return QVector<ShPtrOutputPin>{outputPin_}; } //============================================================== // Получение выходных пинов (константный вариант) //============================================================== QVector<ShPtrConstOutputPin> DumpNode::outputPins() const { return QVector<ShPtrConstOutputPin>{outputPin_}; } //============================================================== // Задание ширины в байтах //============================================================== void DumpNode::setByteWidth(ByteWidths width) { Q_ASSERT(!isUnknown(width)); if (byteWidth_ != width) { const ByteWidths oldWidth = byteWidth_; byteWidth_ = width; const PropValue value{"byteWidth", QVariant::fromValue(byteWidth_)}; emit sigChangedProp(value); if (!isUndo_) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "byteWidth", QVariant::fromValue(byteWidth_), QVariant::fromValue(oldWidth)}; undoStack_->push(undoCmd); } startHighlightTimer(); } } //============================================================== // Сброс ширины в байтах //============================================================== void DumpNode::resetByteWidth() { setByteWidth(ByteWidths::Width16); } //============================================================== // Задание режима //============================================================== void DumpNode::setDumpMode(DumpModes mode) { Q_ASSERT(!isUnknown(mode)); if (dumpMode_ != mode) { const DumpModes oldMode = dumpMode_; dumpMode_ = mode; const PropValue value{"dumpMode", QVariant::fromValue(dumpMode_)}; emit sigChangedProp(value); if (!isUndo_) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "dumpMode", QVariant::fromValue(dumpMode_), QVariant::fromValue(oldMode)}; undoStack_->push(undoCmd); } startHighlightTimer(); } } //============================================================== // Сброс режима //============================================================== void DumpNode::resetDumpMode() { setDumpMode(DumpModes::Binary); } //============================================================== // Задание признака пропуска //============================================================== void DumpNode::setBypass(bool bypass) { if (isBypass_ != bypass) { const bool oldBypass = isBypass_; isBypass_ = bypass; const PropValue value{"bypass", isBypass_}; emit sigChangedProp(value); if (!isUndo_) { Q_ASSERT(undoStack_ != nullptr); const auto undoCmd = new UndoChangeObjectPropValue{this, "bypass", isBypass_, oldBypass}; undoStack_->push(undoCmd); } startHighlightTimer(); } } //============================================================== // Сброс признака пропуска //============================================================== void DumpNode::resetBypass() { setBypass(false); } //============================================================== // Рисование узла //============================================================== void DumpNode::draw(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); drawHighlight(painter, theme); drawBody(painter, theme); drawPins(painter, theme); drawStateArea(painter, theme); drawResizebleMarker(painter, theme); drawComments(painter, theme); } //============================================================== // Получение копии узла //============================================================== ShPtrBaseNode DumpNode::clone() const { const ShPtrDumpNode cloneNode = ShPtrDumpNode::create( undoStack(), parent()); cloneNode->setUndo(true); cloneNode->setLeft(left()); cloneNode->setTop(top()); cloneNode->setWidth(width()); cloneNode->setHeight(height()); cloneNode->setByteWidth(byteWidth()); cloneNode->setDumpMode(dumpMode()); cloneNode->setComment(comment()); cloneNode->setUndo(false); return cloneNode; } //============================================================== // Функция получения имени свойства для графического // интерфейса по его системному имени //============================================================== QString DumpNode::getUiPropertyName(const QString &systemName) { const QMap<QString, QString> map { {"name", tr("Name")}, {"left", tr("Left")}, {"top", tr("Top")}, {"width", tr("Width")}, {"height", tr("Height")}, {"topLeft", tr("Top and left")}, {"size", tr("Size")}, {"comment", tr("Comment")}, {"byteWidth", tr("Byte width")}, {"dumpMode", tr("Dump mode")}, {"bypass", tr("Bypass")} }; return map.value(systemName, tr("Unknown")); } //============================================================== // Функция перевода //============================================================== void DumpNode::retranslate() { updateStateInfo(); } //============================================================== // Функция вызывается при подключении выходного пина //============================================================== void DumpNode::slotOutputPinConnectChanged(ConnectStates state, InputPin *pin) { Q_ASSERT(!isUnknown(state)); Q_ASSERT(pin != nullptr); dataChanged(); const int conNodeId = pin->parentNode()->id(); const int conPinIndex = pin->parentNode()->indexOfInputPin(pin); const bool isCon = ::isConnect(state); emit sigChangedConnect(0, conNodeId, conPinIndex, isCon); } //============================================================== // Создание входного пина //============================================================== void DumpNode::createInputPin() { inputPin_ = ShPtrInputPin::create(this, 0); } //============================================================== // Создание выходного пина //============================================================== void DumpNode::createOutputPin() { outputPin_ = ShPtrOutputPin::create(this, 0); connect(outputPin_.get(), &OutputPin::sigConnectChanged, this, &DumpNode::slotOutputPinConnectChanged); } //============================================================== // Получение признака подключения входного пина //============================================================== bool DumpNode::isConnectedInputPin() const { return inputPin_->isConnected(); } //============================================================== // Получение размера входные данных //============================================================== size_t DumpNode::inputDataSize() const { return isConnectedInputPin() ? inputPin_->dataSize() : 0; } //============================================================== // Обновление состояния узла //============================================================== void DumpNode::updateStateInfo() { const NodeStateInfo oldStateInfo = stateInfo_; if (!isConnectedInputPin()) { const QString msg = "Input pin is not connected"; stateInfo_ = NodeStateInfo{NodeStates::Error, msg}; } else if (isErrorInParent(this)) { const QString msg = "Parent error"; stateInfo_ = NodeStateInfo{NodeStates::Error, msg}; } else if (inputDataSize() == 0) { const QString msg = "No data"; stateInfo_ = NodeStateInfo{NodeStates::Warning, msg}; } else { stateInfo_ = NodeStateInfo{NodeStates::Success, QString{}}; } if (stateInfo_ != oldStateInfo) { emit sigChangedState(stateInfo_); } } //============================================================== // Вывод тела //============================================================== void DumpNode::drawBody(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); // Вывод основы painter->setPen(Qt::transparent); painter->setBrush(Colors::nodeBack(theme)); painter->drawRect(rect().adjusted(-2, -2, 2, 2)); // Вывод типа узла (в режиме пропуска выводится перечёркнутым) painter->setPen(Colors::nodeText(theme)); if (!isBypass()) { painter->drawText(left(), top(), width(), headerHeight(), Qt::AlignCenter, strType()); } else { QFont font = painter->font(); font.setStrikeOut(true); painter->setFont(font); painter->drawText(left(), top(), width(), headerHeight(), Qt::AlignCenter, strType()); font.setStrikeOut(false); painter->setFont(font); } // Вывод данных if (!stateInfo_.isError()) { // Если пропуск данных if (isBypass()) { painter->setPen(Colors::nodeText(theme)); const QString text = "Bypass"; painter->drawText(left(), top() + headerHeight(), width(), height() - headerHeight(), Qt::AlignCenter, text); } // Если данные есть else if (inputDataSize() > 0) { painter->save(); painter->setClipRect(left() + 1, top() + headerHeight(), width() - 2, height() - headerHeight() - gridSize()); const int dataAreaBottom = bottom() - gridSize(); // Заливка области адресов const QColor addressAreaBackColor{255, 255, 230}; const int addressAreaWidth = painter->boundingRect(QRect{}, Qt::AlignCenter, " 00000000 ").width(); painter->drawLine(left() + 1 + addressAreaWidth, top() + headerHeight(), left() + 1 + addressAreaWidth, dataAreaBottom); painter->fillRect(left() + 1, top() + headerHeight(), addressAreaWidth, height() - headerHeight() - gridSize(), addressAreaBackColor); // Вывод полных строк дампа const size_t byteSize = inputDataSize(); const size_t rowCount = byteSize / 16; bool isFillVisibleLines = false; painter->setPen(Colors::nodeText(theme)); for (size_t i = 0; i < rowCount; ++i) { // Формирование данных const ByteList byteLine = dataBlock(i * 16, 16); const QString dataStr = isBinary(dumpMode()) ? bytesToHexLine(byteLine) : bytesToAsciiLine(byteLine); const QRect textRect{left(), top() + headerHeight() + (int)i * charHeight(), width(), height() - headerHeight()}; // Проверка отсечения if (textRect.top() > dataAreaBottom) { isFillVisibleLines = true; break; } // Формирование адреса const quint32 address = calcDataAddress(i, byteWidth()); const QString addressStr = dataAddressToHexStr(address); // Вывод строки с данными painter->drawText(left(), top() + headerHeight() + i * charHeight(), width(), height() - headerHeight(), Qt::AlignLeft | Qt::AlignTop, QString{" %1 %2"}.arg(addressStr, dataStr)); } // Вывод последней строки if (!isFillVisibleLines) { // Формирование данных const size_t lastRowByteCount = byteSize % 16; const ByteList byteLine = dataBlock(rowCount * 16, lastRowByteCount); const QString dataStr = isBinary(dumpMode()) ? bytesToHexLine(byteLine) : bytesToAsciiLine(byteLine); // Формирование адреса const quint32 address = calcDataAddress(rowCount, byteWidth()); const QString addressStr = dataAddressToHexStr(address); // Вывод строки с данными painter->drawText(left(), top() + headerHeight() + rowCount * charHeight(), width(), height() - headerHeight(), Qt::AlignLeft | Qt::AlignTop, QString{" %1 %2"}.arg(addressStr, dataStr)); } painter->restore(); // Нижняя ограничивающая линия painter->drawLine(left() + 1, bottom() - gridSize(), right() - 2, bottom() - gridSize()); } // Если данных нет else { painter->setPen(Colors::nodeText(theme)); const QString text = "Not data"; painter->drawText(left(), top() + headerHeight(), width(), height() - headerHeight() - gridSize(), Qt::AlignCenter, text); // Нижняя ограничивающая линия painter->drawLine(left() + 1, bottom() - gridSize(), right() - 2, bottom() - gridSize()); } } else { painter->setPen(Colors::nodeText(theme)); const QString strState = nodeStateToStr(stateInfo().state()); painter->drawText(left(), top() + headerHeight(), width(), height() - headerHeight(), Qt::AlignCenter, strState); } // Вывод разделительной линии painter->setPen(Colors::nodeBorder(theme)); painter->drawLine(left() + 1, top() + headerHeight(), right() - 2, top() + headerHeight()); // Обводка контура QPen borderPen{currentBorderColor(theme), 2}; borderPen.setJoinStyle(Qt::MiterJoin); painter->setPen(borderPen); painter->setBrush(Qt::transparent); painter->drawRect(rect()); // Дополнительная обводка для того, чтобы узел хорошо // смотрелся на любом фоне // painter->setPen(Colors::nodeBack(theme)); // painter->setBrush(Qt::transparent); // painter->drawRect(rect().adjusted(-2, -2, 1, 1)); } //============================================================== // Вывод комментариев //============================================================== void DumpNode::drawComments(QPainter *painter, ColorThemes theme) const { Q_ASSERT(painter != nullptr); Q_ASSERT(!isUnknown(theme)); if (isCommentsVisible()) { QString comments{}; comments += QString{" <<< %1: \"%2\"\n"}.arg(tr("Name"), name()); comments += QString{" %1: %2\n"}.arg(tr("Mode"), dumpModeToStrTr(dumpMode())); comments += QString{" %1: %2\n"}.arg(tr("Width (B)")).arg(byteWidthToInt(byteWidth())); comments += QString{" %1: %2\n"}.arg(tr("Bypass"), boolToStrTr(isBypass())); comments += QString{" %1: \"%2\"\n"}.arg(tr("Comment"), comment()); #ifdef QT_DEBUG comments += " -\n"; comments += QString{" %1: %2\n"}.arg("Id").arg(id()); #endif // QT_DEBUG const int commentsLeft = right(); const int commentsTop = top() + (headerHeight() - charHeight()) / 2; const int commentsFlags = Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip; const QRect commentsRect{commentsLeft, commentsTop, 0, 0}; painter->setPen(Colors::nodeText(theme)); painter->drawText(commentsRect, commentsFlags, comments); } } //////////////////////////////////////////////////////////////// // Реализация функций //////////////////////////////////////////////////////////////// //============================================================== // Конвертация набора байтов в хекс-строку //============================================================== QString bytesToHexLine(const ByteList &bytes) { Q_ASSERT(bytes.size() <= 16); QString line{}; line.reserve(static_cast<int>(bytes.size()) * 2); for (size_t i = 0; i < bytes.size(); ++i) { line += byteToStr(bytes[i]); if (i == 7) { line += " "; } else { line += " "; } } return line; } //============================================================== // Конвертация набора байтов в ASCII-строку //============================================================== QString bytesToAsciiLine(const ByteList &bytes) { Q_ASSERT(bytes.size() <= 16); QString line{}; line.reserve(static_cast<int>(bytes.size())); for (size_t i = 0; i < bytes.size(); ++i) { line += intToAscii(bytes[i]); } return line; } //============================================================== // Расчёт адреса данных по параметрам //============================================================== quint32 calcDataAddress(int lineIndex, ByteWidths byteWidth) { Q_ASSERT(!isUnknown(byteWidth)); const int byteInLine = byteWidthToInt(byteWidth); return static_cast<quint32>(lineIndex) * byteInLine; } //============================================================== // Конвертация адреса данных в строку //============================================================== QString dataAddressToHexStr(quint32 address) { return QString("%1").arg(address, 8, 16, QChar{'0'}).toUpper(); }