Files
GostGenerator/controller/simplelisttablecontroller.cpp
T
2026-09-02 12:48:03 +03:00

649 lines
28 KiB
C++

#include "simplelisttablecontroller.h"
#include "../model/componentmodel.h"
#include "../model/specificationpcbtablemodel.h"
#include <QDebug>
#include <QCollator>
#include <QRegExp>
SimpleListTableController::SimpleListTableController(QObject *parent)
: QObject(parent)
, m_tableModel(new SimpleListTableModel(this))
, m_nameField("Name")
, m_techReservePercent(10)
, m_boardsCount(1)
{
qDebug() << "SimpleListTableController::SimpleListTableController: Создан контроллер";
}
SimpleListTableController::~SimpleListTableController()
{
// m_tableModel удалится автоматически, так как он является дочерним объектом
}
void SimpleListTableController::setModel(SimpleListTableModel *model)
{
if (m_tableModel != model) {
qDebug() << "SimpleListTableController::setModel: Заменяем модель таблицы";
m_tableModel = model;
// Синхронизируем процент тех запаса с моделью
if (m_tableModel) {
m_tableModel->setTechReservePercent(m_techReservePercent);
}
}
}
void SimpleListTableController::setComponents(const QList<ComponentModel*> &components)
{
qDebug() << "SimpleListTableController::setComponents: Устанавливаем" << components.size() << "компонентов";
m_components = components;
// Собираем доступные свойства из компонентов
m_propertyNames = collectPropertyNames();
qDebug() << "SimpleListTableController::setComponents: Собрано свойств:" << m_propertyNames.size();
}
void SimpleListTableController::generateTableFromComponents()
{
if (!m_tableModel) {
return;
}
qDebug() << "SimpleListTableController::generateTableFromComponents: Начинаем генерацию таблицы";
qDebug() << "SimpleListTableController::generateTableFromComponents: Количество компонентов:" << m_components.size();
// Очищаем таблицу
m_tableModel->clear();
if (m_components.isEmpty()) {
return;
}
try {
// Сортируем компоненты по десигнатору
std::sort(m_components.begin(), m_components.end(),
[this](ComponentModel* a, ComponentModel* b) {
if (!a || !b) return false;
QString designatorA = a->designator();
QString designatorB = b->designator();
// Разделяем десигнаторы на буквенную и числовую части
QPair<QString, QString> partsA = splitDesignator(designatorA);
QPair<QString, QString> partsB = splitDesignator(designatorB);
QString letterA = partsA.first;
QString letterB = partsB.first;
QString numberA = partsA.second;
QString numberB = partsB.second;
// Сначала сравниваем буквенные части
if (letterA != letterB) {
return letterA < letterB;
}
// Если буквенные части одинаковые, сравниваем числовые части
bool okA, okB;
int numA = numberA.toInt(&okA);
int numB = numberB.toInt(&okB);
if (okA && okB) {
return numA < numB;
}
// Fallback: если не удалось преобразовать в числа, используем строковое сравнение
return numberA < numberB;
});
// Группируем компоненты по наименованию (Name)
QString prevName = "";
QString groupStartDesignator = "";
QString groupLastDesignator = "";
int groupCount = 0;
for (int i = 0; i < m_components.size(); ++i) {
ComponentModel *component = m_components[i];
if (!component) {
qDebug() << "SimpleListTableController::generateTableFromComponents: Пропускаем nullptr компонент на позиции" << i;
continue;
}
QString currentName = getComponentValue(component, m_nameField);
if (i == 0 || currentName != prevName) {
// Добавляем предыдущую группу, если она есть
if (i > 0 && groupCount > 0) {
SimpleListRowData row;
// Формируем строку десигнаторов
QString designatorText;
if (groupCount == 1) {
designatorText = groupStartDesignator;
} else if (groupCount == 2) {
designatorText = groupStartDesignator + ", " + groupLastDesignator;
} else {
designatorText = groupStartDesignator + "-" + groupLastDesignator;
}
row.designator = SimpleListCellData(designatorText, false, false);
row.name = SimpleListCellData(prevName, false, false);
// (кол-во элементов * кол-во плат) + техзапас
int baseQty = groupCount * m_boardsCount;
int reserve = qMax(1, qRound(baseQty * m_techReservePercent / 100.0));
int quantityWithReserve = baseQty + reserve;
row.quantity = SimpleListCellData(QString::number(quantityWithReserve), false, false);
if (m_tableModel) {
m_tableModel->addRow(row);
}
}
// Начинаем новую группу
groupStartDesignator = component->designator();
groupLastDesignator = component->designator();
groupCount = 1;
prevName = currentName;
} else {
// Продолжаем текущую группу - обновляем последний десигнатор
groupLastDesignator = component->designator();
groupCount++;
}
}
// Добавляем последнюю группу
if (groupCount > 0 && m_tableModel) {
SimpleListRowData row;
QString designatorText;
if (groupCount == 1) {
designatorText = groupStartDesignator;
} else if (groupCount == 2) {
designatorText = groupStartDesignator + ", " + groupLastDesignator;
} else {
designatorText = groupStartDesignator + "-" + groupLastDesignator;
}
row.designator = SimpleListCellData(designatorText, false, false);
row.name = SimpleListCellData(prevName, false, false);
// (кол-во элементов * кол-во плат) + техзапас
int baseQty = groupCount * m_boardsCount;
int reserve = qMax(1, qRound(baseQty * m_techReservePercent / 100.0));
int quantityWithReserve = baseQty + reserve;
row.quantity = SimpleListCellData(QString::number(quantityWithReserve), false, false);
m_tableModel->addRow(row);
}
} catch (const std::exception &e) {
qDebug() << "SimpleListTableController::generateTableFromComponents: Ошибка:" << e.what();
}
qDebug() << "SimpleListTableController::generateTableFromComponents: Генерация завершена";
}
void SimpleListTableController::generateTableFromSpecificationPCB(SpecificationPCBTableModel *specPCBModel)
{
if (!m_tableModel || !specPCBModel) {
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Модель недоступна";
return;
}
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Начинаем генерацию из SpecificationPCB";
// Очищаем таблицу
m_tableModel->clear();
// Получаем все строки из SpecificationPCB
QList<SpecificationPCBRowData> allRows = specPCBModel->getRows();
if (allRows.isEmpty()) {
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Нет данных в SpecificationPCB";
return;
}
// Находим индекс строки с "Прочие изделия"
int startIndex = -1;
for (int i = 0; i < allRows.size(); ++i) {
if (allRows[i].name.value == "Прочие изделия" && allRows[i].isHeader) {
startIndex = i;
break;
}
}
if (startIndex == -1) {
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Не найдена строка 'Прочие изделия'";
return;
}
// Находим индекс строки с "Примечание"
int endIndex = -1;
for (int i = startIndex + 1; i < allRows.size(); ++i) {
if (allRows[i].name.value == "Примечание" && !allRows[i].isHeader) {
endIndex = i;
break;
}
}
if (endIndex == -1) {
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Не найдена строка 'Примечание'";
// Если не найдено "Примечание", берем все строки до конца
endIndex = allRows.size();
}
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Найдены строки от" << startIndex << "до" << endIndex;
// Извлекаем строки между "Прочие изделия" и "Примечание"
// Пропускаем саму строку "Прочие изделия" и пустые строки после нее
QList<SpecificationPCBRowData> relevantRows;
for (int i = startIndex + 1; i < endIndex; ++i) {
// Пропускаем пустые строки
if (allRows[i].isEmpty) {
continue;
}
// Пропускаем заголовки
if (allRows[i].isHeader) {
continue;
}
relevantRows.append(allRows[i]);
}
if (relevantRows.isEmpty()) {
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Нет релевантных строк";
return;
}
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Найдено релевантных строк:" << relevantRows.size();
// Группируем строки по позиции (position)
// Строки с одинаковой позицией должны быть склеены вместе
// Используем QMap для группировки, но обрабатываем в порядке появления
QMap<QString, QList<SpecificationPCBRowData>> groupedByPosition;
QStringList positionOrder; // Для сохранения порядка позиций
QString currentPosition;
for (const SpecificationPCBRowData &row : relevantRows) {
QString position = row.position.value.trimmed();
// Если позиция пуста, это продолжение предыдущей позиции
if (position.isEmpty()) {
// Добавляем к текущей позиции
if (!currentPosition.isEmpty()) {
groupedByPosition[currentPosition].append(row);
}
} else {
// Новая позиция
currentPosition = position;
if (!groupedByPosition.contains(position)) {
groupedByPosition[position] = QList<SpecificationPCBRowData>();
positionOrder.append(position); // Сохраняем порядок
}
groupedByPosition[position].append(row);
}
}
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Сгруппировано позиций:" << groupedByPosition.size();
// Обрабатываем каждую группу позиций в порядке появления
for (const QString &position : positionOrder) {
if (!groupedByPosition.contains(position)) {
continue;
}
QList<SpecificationPCBRowData> rowsForPosition = groupedByPosition[position];
if (rowsForPosition.isEmpty()) {
continue;
}
// Склеиваем наименование (name) и примечание (note) для всех строк этой позиции
QStringList nameParts;
QStringList noteParts;
for (const SpecificationPCBRowData &row : rowsForPosition) {
QString nameValue = row.name.value.trimmed();
QString noteValue = row.note.value.trimmed();
if (!nameValue.isEmpty()) {
nameParts.append(nameValue);
}
// Примечание (десигнаторы): разбиваем по переносам строк внутри ячейки (R1, R3\nR5 -> R1, R3, R5)
if (!noteValue.isEmpty()) {
QString normalized = noteValue.replace("\r\n", "\n").replace('\r', '\n');
for (const QString &line : normalized.split('\n', Qt::SkipEmptyParts)) {
QString trimmed = line.trimmed();
if (!trimmed.isEmpty()) {
noteParts.append(trimmed);
}
}
}
}
// Объединяем части наименования и примечания обратно в одну строку
QString fullName = nameParts.join(" "); // Склеиваем многострочные наименования
QString fullNote = noteParts.join(", "); // Десигнаторы (R1, R3, R5) — между частями после переноса ставим запятую
// Получаем количество из первой строки позиции (остальные строки обычно пустые по количеству)
int quantity = 0;
for (const SpecificationPCBRowData &row : rowsForPosition) {
QString qtyStr = row.quantity.value.trimmed();
if (!qtyStr.isEmpty()) {
bool ok;
int qty = qtyStr.toInt(&ok);
if (ok && qty > 0) {
quantity = qty;
break;
}
}
}
// Если количество не найдено, считаем количество строк с номерами позиций
if (quantity == 0) {
quantity = 1; // По умолчанию 1
}
// (кол-во элементов * кол-во плат) + техзапас
int baseQty = quantity * m_boardsCount;
int reserve = qMax(1, qRound(baseQty * m_techReservePercent / 100.0));
int quantityWithReserve = baseQty + reserve;
// Создаем строку для SimpleList
SimpleListRowData row;
row.designator = SimpleListCellData(fullNote, false, false); // Поз.обозначение из примечания (десигнаторы)
row.name = SimpleListCellData(fullName, false, false); // Наименование (склеенное из всех строк позиции)
row.quantity = SimpleListCellData(QString::number(quantityWithReserve), false, false);
m_tableModel->addRow(row);
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Добавлена строка - Позиция:" << position
<< "Поз.обозначение:" << fullNote << "Наименование:" << fullName << "Кол-во:" << quantityWithReserve;
}
qDebug() << "SimpleListTableController::generateTableFromSpecificationPCB: Генерация завершена, строк:" << m_tableModel->rowCount();
}
void SimpleListTableController::updateTableFromComponents()
{
generateTableFromComponents();
}
void SimpleListTableController::addEmptyRow()
{
if (!m_tableModel) {
return;
}
m_tableModel->addEmptyRow();
}
void SimpleListTableController::addEmptyRowAt(int position)
{
if (!m_tableModel) {
return;
}
m_tableModel->addEmptyRowAt(position);
}
void SimpleListTableController::insertEmptyRowAt(int position)
{
if (!m_tableModel) {
return;
}
m_tableModel->addEmptyRowAt(position);
}
void SimpleListTableController::removeRow(int row)
{
if (!m_tableModel) {
return;
}
m_tableModel->removeRow(row);
}
void SimpleListTableController::clearTable()
{
if (!m_tableModel) {
return;
}
m_tableModel->clear();
}
QString SimpleListTableController::getComponentValue(ComponentModel *component, const QString &propertyName)
{
if (!component) {
return "";
}
// Получаем значение свойства компонента
QString value;
if (propertyName == "Designator") {
value = component->designator();
} else {
QMap<QString, QString> props = component->properties();
value = props.value(propertyName, "");
}
// Проверяем, является ли значение свойства выражением
if (value.startsWith("=")) {
qDebug() << "SimpleListTableController::getComponentValue: Значение свойства является выражением:" << value;
return parseExpression(component, value);
}
return value;
}
QString SimpleListTableController::parseExpression(ComponentModel *component, const QString &expression)
{
if (!component) {
return "";
}
qDebug() << "SimpleListTableController::parseExpression: Обрабатываем выражение:" << expression;
QString result;
QString expr;
// Определяем, как обрабатывать выражение
if (expression.startsWith("=")) {
expr = expression.mid(1); // Убираем начальный "="
} else if (expression.startsWith("\"") && expression.endsWith("\"")) {
// Если это просто значение в кавычках, возвращаем его без кавычек
return expression.mid(1, expression.length() - 2);
} else {
// Если это не выражение, возвращаем как есть
return expression;
}
// Разбиваем выражение по операторам "+" с учетом кавычек
QStringList parts;
QString currentPart;
bool inDoubleQuotes = false;
bool inSingleQuotes = false;
for (int i = 0; i < expr.length(); ++i) {
QChar ch = expr[i];
if (ch == '"' && !inSingleQuotes) {
inDoubleQuotes = !inDoubleQuotes;
currentPart += ch;
} else if (ch == '\'' && !inDoubleQuotes) {
inSingleQuotes = !inSingleQuotes;
currentPart += ch;
} else if (ch == '+' && !inDoubleQuotes && !inSingleQuotes) {
// Это разделитель "+" вне кавычек
if (!currentPart.trimmed().isEmpty()) {
parts.append(currentPart.trimmed());
}
currentPart.clear();
} else {
currentPart += ch;
}
}
// Добавляем последнюю часть
if (!currentPart.trimmed().isEmpty()) {
parts.append(currentPart.trimmed());
}
qDebug() << "SimpleListTableController::parseExpression: Части выражения:" << parts;
for (const QString &part : parts) {
QString trimmedPart = part.trimmed();
qDebug() << "SimpleListTableController::parseExpression: Обрабатываем часть:" << trimmedPart;
// Если это не пустая строка, добавляем к результату
if (!trimmedPart.isEmpty()) {
QString value;
// Проверяем, является ли это литералом в одинарных кавычках
if (trimmedPart.startsWith("'") && trimmedPart.endsWith("'")) {
// Это литерал - убираем кавычки и используем как есть
value = trimmedPart.mid(1, trimmedPart.length() - 2);
qDebug() << "SimpleListTableController::parseExpression: Найден литерал в одинарных кавычках:" << value;
}
// Проверяем, является ли это именем свойства компонента в двойных кавычках
else if (trimmedPart.startsWith("\"") && trimmedPart.endsWith("\"")) {
// Это свойство компонента - убираем кавычки и ищем в свойствах
QString propertyName = trimmedPart.mid(1, trimmedPart.length() - 2);
qDebug() << "SimpleListTableController::parseExpression: Ищем свойство компонента:" << propertyName;
if (propertyName == "Designator") {
value = component->designator();
qDebug() << "SimpleListTableController::parseExpression: Найдено свойство Designator =" << value;
} else {
// Ищем в свойствах компонента
QMap<QString, QString> props = component->properties();
value = props.value(propertyName, "");
qDebug() << "SimpleListTableController::parseExpression: Ищем свойство" << propertyName << "в компоненте, результат:" << value;
// Рекурсивно обрабатываем вложенные выражения
if (value.startsWith("=")) {
qDebug() << "SimpleListTableController::parseExpression: Значение свойства является вложенным выражением, рекурсивно обрабатываем";
value = parseExpression(component, value);
}
}
}
// Проверяем, является ли это именем свойства компонента без кавычек
else if (trimmedPart == "Designator") {
value = component->designator();
qDebug() << "SimpleListTableController::parseExpression: Найдено свойство Designator =" << value;
} else {
// Ищем в свойствах компонента
QMap<QString, QString> props = component->properties();
value = props.value(trimmedPart, "");
qDebug() << "SimpleListTableController::parseExpression: Ищем свойство" << trimmedPart << "в компоненте, результат:" << value;
// Рекурсивно обрабатываем вложенные выражения
if (value.startsWith("=")) {
qDebug() << "SimpleListTableController::parseExpression: Значение свойства является вложенным выражением, рекурсивно обрабатываем";
value = parseExpression(component, value);
}
}
// Добавляем значение к результату
if (!value.isEmpty()) {
if (!result.isEmpty()) {
result += value;
} else {
result = value;
}
qDebug() << "SimpleListTableController::parseExpression: Добавили к результату, текущий результат:" << result;
}
}
}
qDebug() << "SimpleListTableController::parseExpression: Финальный результат:" << result;
return result;
}
QStringList SimpleListTableController::collectPropertyNames()
{
QStringList propertyNames;
// Добавляем основные свойства компонента
propertyNames << "Designator";
// Собираем дополнительные свойства из компонентов
for (ComponentModel *component : m_components) {
QMap<QString, QString> props = component->properties();
for (auto it = props.begin(); it != props.end(); ++it) {
if (!propertyNames.contains(it.key())) {
propertyNames.append(it.key());
}
}
}
// Если дополнительные свойства не найдены в компонентах, добавляем стандартные
if (propertyNames.size() == 1) { // Только Designator
propertyNames << "Name" << "Type" << "Value" << "Footprint" << "Description";
}
return propertyNames;
}
QPair<QString, QString> SimpleListTableController::splitDesignator(const QString &designator)
{
if (designator.isEmpty()) {
return QPair<QString, QString>("", "");
}
// Используем регулярное выражение для разделения буквенной и цифровой части
QRegExp regex("([A-Za-z]+)(\\d*)");
if (regex.indexIn(designator) != -1) {
QString letterPart = regex.cap(1);
QString numberPart = regex.cap(2);
return QPair<QString, QString>(letterPart, numberPart);
}
// Fallback: если регулярное выражение не сработало,
// ищем первую цифру и разделяем по ней
for (int i = 0; i < designator.length(); ++i) {
if (designator[i].isDigit()) {
QString letterPart = designator.left(i);
QString numberPart = designator.mid(i);
return QPair<QString, QString>(letterPart, numberPart);
}
}
// Если цифр нет, возвращаем весь десигнатор как буквенную часть
return QPair<QString, QString>(designator, "");
}
void SimpleListTableController::setNameField(const QString &fieldName)
{
qDebug() << "SimpleListTableController::setNameField: Устанавливаем поле наименования:" << fieldName;
m_nameField = fieldName;
}
QString SimpleListTableController::getNameField() const
{
return m_nameField;
}
void SimpleListTableController::setTechReservePercent(int percent)
{
qDebug() << "SimpleListTableController::setTechReservePercent: Устанавливаем процент тех запаса:" << percent;
m_techReservePercent = percent;
// Синхронизируем с моделью для обновления заголовка
if (m_tableModel) {
m_tableModel->setTechReservePercent(percent);
}
}
int SimpleListTableController::getTechReservePercent() const
{
return m_techReservePercent;
}
void SimpleListTableController::setBoardsCount(int count)
{
if (count < 1) {
count = 1;
}
m_boardsCount = count;
}
int SimpleListTableController::getBoardsCount() const
{
return m_boardsCount;
}
QStringList SimpleListTableController::getAvailableProperties() const
{
return m_propertyNames;
}