Files
GostGenerator/controller/altiumparser.cpp
T

1199 lines
43 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 "altiumparser.h"
#include "databasecontroller.h"
#include "../model/componentmodel.h"
#include <QFileInfo>
#include <QFile>
#include <QTextStream>
#include <QRegularExpression>
#include <QTextCodec>
#include <QDir>
#include <QMessageBox>
AltiumParser::AltiumParser(QObject *parent)
: QObject(parent)
, m_isPcbMultilayer(false)
, m_database(nullptr)
, m_parseWatcher(nullptr)
, m_isParsingAsync(false)
, m_pcbDocFileName("")
{
initializeRegex();
// Инициализируем watcher для асинхронного парсинга
m_parseWatcher = new QFutureWatcher<bool>(this);
connect(m_parseWatcher, &QFutureWatcher<bool>::finished,
this, &AltiumParser::onParseFinished);
connect(m_parseWatcher, &QFutureWatcher<bool>::progressValueChanged,
this, &AltiumParser::onParseProgress);
}
AltiumParser::~AltiumParser()
{
}
bool AltiumParser::parseSchFile(const QString &filename, ComponentModel *model)
{
if (!model) {
setError("Модель компонентов не инициализирована");
return false;
}
// TODO: Реализовать парсинг .sch файлов
setError("Парсинг .sch файлов еще не реализован");
return false;
}
bool AltiumParser::parsePcbFile(const QString &filename, ComponentModel *model)
{
if (!model) {
setError("Модель компонентов не инициализирована");
return false;
}
// TODO: Реализовать парсинг .pcb файлов
setError("Парсинг .pcb файлов еще не реализован");
return false;
}
bool AltiumParser::parseBomFile(const QString &filename, ComponentModel *model)
{
if (!model) {
setError("Модель компонентов не инициализирована");
return false;
}
// TODO: Реализовать парсинг BOM файлов
setError("Парсинг BOM файлов еще не реализован");
return false;
}
bool AltiumParser::parseSchDoc(const QString &filename, ComponentModel *model)
{
if (!model) {
setError("Модель компонентов не инициализирована");
return false;
}
// TODO: Реализовать парсинг .SchDoc файлов
setError("Парсинг .SchDoc файлов еще не реализован");
return false;
}
bool AltiumParser::parsePcbDoc(const QString &filename, ComponentModel *model)
{
if (!model) {
setError("Модель компонентов не инициализирована");
return false;
}
// TODO: Реализовать парсинг .PcbDoc файлов
setError("Парсинг .PcbDoc файлов еще не реализован");
return false;
}
bool AltiumParser::parseCsvBom(const QString &filename, ComponentModel *model)
{
if (!model) {
setError("Модель компонентов не инициализирована");
return false;
}
// TODO: Реализовать парсинг CSV BOM файлов
setError("Парсинг CSV BOM файлов еще не реализован");
return false;
}
bool AltiumParser::parseXlsBom(const QString &filename, ComponentModel *model)
{
if (!model) {
setError("Модель компонентов не инициализирована");
return false;
}
// TODO: Реализовать парсинг XLS BOM файлов
setError("Парсинг XLS BOM файлов еще не реализован");
return false;
}
// Новый метод для парсинга PrjPcb файлов
bool AltiumParser::parsePrjPcbFile(const QString &filename, ComponentModel *model)
{
if (!model) {
setError("Модель компонентов не инициализирована");
return false;
}
if (!QFile::exists(filename)) {
setError("Файл не существует: " + filename);
return false;
}
clearError();
emit parsingProgress(0);
// Очищаем предыдущие данные
m_componentsList.clear();
m_variantsList.clear();
m_dielMaterialsList.clear();
m_projectParamsList.clear();
m_variantNamesList.clear();
m_isPcbMultilayer = false;
// Очищаем базу данных
if (m_database) {
m_database->clearAllData();
}
emit parsingProgress(10);
bool success = parseProjectFile(filename, model);
if (success) {
emit parsingProgress(100);
emit parsingFinished(true);
} else {
emit parsingFinished(false);
}
return success;
}
bool AltiumParser::parseProjectFile(const QString &filename, ComponentModel *model)
{
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
setError("Не удалось открыть файл: " + filename);
return false;
}
QTextStream reader(&file);
reader.setCodec(QTextCodec::codecForName("Windows-1251"));
// Очищаем базу данных
if (m_database) {
m_database->clearAllData();
}
// Сначала подсчитываем общее количество строк для равномерного прогресса
QFile countFile(filename);
countFile.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream countReader(&countFile);
countReader.setCodec(QTextCodec::codecForName("Windows-1251"));
int totalLines = 0;
while (!countReader.atEnd()) {
countReader.readLine();
totalLines++;
}
countFile.close();
// Открываем файл заново для парсинга
file.seek(0);
// Инициализация структур данных
ProjectData projectData;
projectData.variantNames.append("No Variations");
projectData.pcbLayerCount = 0;
projectData.pcbDielMaterials.clear();
QString prevPrjStr, prevPrevPrjStr;
int lineCount = 0;
int lastProgress = 0;
while (!reader.atEnd()) {
QString prjStr = reader.readLine().trimmed();
lineCount++;
// Обновляем прогресс каждые 100 строк или при достижении определенного процента
int currentProgress = (lineCount * 80) / totalLines; // 80% для парсинга
if (currentProgress > lastProgress) {
emit parsingProgress(currentProgress);
lastProgress = currentProgress;
}
// Обработка различных типов строк
parseProjectVariantSection(prjStr, projectData);
parseComponentVariations(prjStr, projectData);
parseProjectParameters(prevPrevPrjStr, prevPrjStr, prjStr, projectData);
parseSchDocFiles(prjStr, filename, projectData);
parsePcbDocFiles(prjStr, filename, projectData);
prevPrevPrjStr = prevPrjStr;
prevPrjStr = prjStr;
}
file.close();
// Вывод результатов парсинга
printParsingResults(projectData);
// Сохранение данных в базу (20% прогресса)
emit parsingProgress(80);
saveDataToDatabase(projectData);
// Сохраняем имя PcbDoc файла
m_pcbDocFileName = projectData.pcbDocFileName;
// Установка данных в модель
emit parsingProgress(90);
setupModelData(projectData, model);
return true;
}
// Обработка секций вариантов проекта
void AltiumParser::parseProjectVariantSection(const QString &prjStr, ProjectData &data)
{
// Обработка [ProjectVariant]
if (prjStr.length() > 15 && prjStr.left(15).toUpper() == "[PROJECTVARIANT") {
data.componentVariantPropList.clear();
data.isWaitingVariantDescription = true;
QString cleanStr = prjStr;
cleanStr.replace(']', QChar());
data.currentVariantNumber = cleanStr.mid(15).toInt();
}
// Обработка ParamVariationCount
if (prjStr.length() > 18 && prjStr.left(19).toUpper() == "PARAMVARIATIONCOUNT") {
finalizeCurrentVariant(data);
}
// Обработка Description
if (prjStr.length() > 12 && data.isWaitingVariantDescription &&
prjStr.left(12).toUpper() == "DESCRIPTION=") {
data.isWaitingVariantDescription = false;
QString variantName = prjStr.mid(12);
data.variantNames.append(variantName);
}
}
// Завершение текущего варианта
void AltiumParser::finalizeCurrentVariant(ProjectData &data)
{
if (data.componentVariantPropList.size() > 0) {
data.componentsPropVariantList.append(data.componentVariantPropList);
data.componentVariantPropList.clear();
}
if (data.componentsPropVariantList.size() > 0) {
data.componentsVariantList.append(data.componentsPropVariantList);
data.componentsPropVariantList.clear();
}
data.dnfVariantDesignatorsList.append(data.dnfDesignatorsList);
data.fittedVariantDesignatorsList.append(data.fittedDesignatorsList);
data.dnfDesignatorsList.clear();
data.fittedDesignatorsList.clear();
}
// Обработка вариаций компонентов
void AltiumParser::parseComponentVariations(const QString &prjStr, ProjectData &data)
{
if (prjStr.length() <= 9) return;
QStringList prjStrArray = prjStr.split('|');
if (prjStrArray.isEmpty()) return;
QStringList valuesArray = prjStrArray[0].split('=');
if (valuesArray.size() <= 2) return;
// Обработка VARIATION=DESIGNATOR
if (valuesArray[0].length() >= 9 && valuesArray[0].left(9).toUpper() == "VARIATION") {
if (valuesArray[1].left(10).toUpper() == "DESIGNATOR") {
parseVariationDesignator(prjStrArray, valuesArray, data);
}
}
// Обработка PARAMVARIATION=PARAMETERNAME
if (valuesArray[0].length() >= 14 && valuesArray[0].left(14).toUpper() == "PARAMVARIATION") {
if (valuesArray[1].toUpper() == "PARAMETERNAME") {
parseParamVariation(prjStrArray, valuesArray, data);
}
}
}
// Парсинг VARIATION=DESIGNATOR
void AltiumParser::parseVariationDesignator(const QStringList &prjStrArray,
const QStringList &valuesArray,
ProjectData &data)
{
if (data.componentVariantPropList.size() > 0) {
data.componentsPropVariantList.append(data.componentVariantPropList);
data.componentVariantPropList.clear();
}
ComponentProperty prop;
prop.name = "Designator";
prop.text = valuesArray[2];
QString designator = valuesArray[2];
data.componentVariantPropList.append(prop);
// Обработка KIND
if (prjStrArray.size() > 1) {
QStringList kindValues = prjStrArray[2].split('=');
if (kindValues[0].toUpper() == "KIND") {
ComponentProperty kindProp;
kindProp.name = "Kind";
kindProp.text = kindValues[1];
data.componentVariantPropList.append(kindProp);
if (kindValues[1] == "1") {
data.dnfDesignatorsList.append(designator);
} else {
data.fittedDesignatorsList.append(designator);
}
}
}
}
// Парсинг PARAMVARIATION=PARAMETERNAME
void AltiumParser::parseParamVariation(const QStringList &prjStrArray,
const QStringList &valuesArray,
ProjectData &data)
{
ComponentProperty prop;
prop.name = valuesArray[2];
// Считываем VariantValue
int idx = prjStrArray[1].indexOf('=');
if (idx >= 0) {
prop.text = prjStrArray[1].mid(idx + 1);
} else {
prop.text = prjStrArray[1];
}
data.componentVariantPropList.append(prop);
}
// Обработка параметров проекта
void AltiumParser::parseProjectParameters(const QString &prevPrevPrjStr,
const QString &prevPrjStr,
const QString &prjStr,
ProjectData &data)
{
if (prevPrevPrjStr.length() <= 10 || prevPrjStr.length() <= 4 || prjStr.length() <= 5) {
return;
}
if (prevPrevPrjStr.left(10).toUpper() == "[PARAMETER" &&
prevPrjStr.left(4).toUpper() == "NAME" &&
prjStr.left(5).toUpper() == "VALUE") {
QString cleanPrevPrev = prevPrevPrjStr;
cleanPrevPrev.replace(']', QChar());
QStringList paramPrjStrArray = cleanPrevPrev.split('_');
int variantNumber = (paramPrjStrArray.size() > 1) ? data.currentVariantNumber : 0;
QStringList nameValue;
nameValue.append(prevPrjStr.split('=')[1]);
nameValue.append(prjStr.split('=')[1]);
while (data.prjParamsVariantList.size() <= variantNumber) {
data.prjParamsVariantList.append(QList<QStringList>());
}
data.prjParamsVariantList[variantNumber].append(nameValue);
}
}
// Обработка файлов схем
void AltiumParser::parseSchDocFiles(const QString &prjStr, const QString &filename, ProjectData &data)
{
if (prjStr.length() <= 13) return;
if (prjStr.left(13).toUpper() == "DOCUMENTPATH=" && prjStr.right(6).toUpper() == "SCHDOC") {
QString schPath = QDir(QFileInfo(filename).absolutePath()).filePath(prjStr.mid(13));
if (QFile::exists(schPath)) {
int componentsBefore = data.componentsList.size();
parseSchDocFile(schPath, data.componentsList);
int componentsAfter = data.componentsList.size();
}
}
}
// Обработка файлов плат
void AltiumParser::parsePcbDocFiles(const QString &prjStr, const QString &filename, ProjectData &data)
{
if (prjStr.length() <= 13) return;
if (prjStr.left(13).toUpper() == "DOCUMENTPATH=" && prjStr.right(6).toUpper() == "PCBDOC") {
QString pcbPath = QDir(QFileInfo(filename).absolutePath()).filePath(prjStr.mid(13));
if (QFile::exists(pcbPath)) {
bool parseResult = parsePcbDocFile(pcbPath, data.pcbDielMaterials, data.pcbLayerCount);
// Сохраняем имя PcbDoc файла
if (parseResult) {
data.pcbDocFileName = pcbPath;
}
}
}
}
// Вывод результатов парсинга
void AltiumParser::printParsingResults(const ProjectData &data)
{
}
// Сохранение данных в базу данных
void AltiumParser::saveDataToDatabase(const ProjectData &data)
{
if (!m_database) {
return;
}
// Сначала создаем варианты в БД (80-85%)
emit parsingProgress(80);
saveVariantsToDatabase(data);
// Сохраняем обычные компоненты (85-90%)
emit parsingProgress(85);
saveRegularComponents(data);
// Сохраняем вариативные свойства (90-95%)
emit parsingProgress(90);
saveVariantProperties(data);
// Сохраняем параметры проекта (95-100%)
emit parsingProgress(95);
saveProjectParameters(data);
// Сохраняем данные PCB (97-100%)
emit parsingProgress(97);
savePCBDataToDatabase(data);
emit parsingProgress(100);
}
// Сохранение вариантов в базу данных
void AltiumParser::saveVariantsToDatabase(const ProjectData &data)
{
for (const QString &variantName : data.variantNames) {
// Проверяем, существует ли вариант
int variantId = m_database->getVariantId(variantName);
if (variantId == -1) {
// Создаем новый вариант
variantId = m_database->addVariant(variantName);
}
}
}
// Сохранение обычных компонентов
void AltiumParser::saveRegularComponents(const ProjectData &data)
{
if (data.componentsList.isEmpty()) {
return;
}
// Начинаем транзакцию для пакетной вставки
if (!m_database->beginTransaction()) {
return;
}
int savedComponents = 0;
for (const QList<ComponentProperty> &componentProps : data.componentsList) {
QString designator;
QMap<QString, QString> properties;
for (const ComponentProperty &prop : componentProps) {
if (prop.name == "Designator") {
designator = prop.text;
} else {
properties[prop.name] = prop.text;
}
}
if (!designator.isEmpty()) {
// Используем пакетную вставку
bool success = m_database->addComponentBatch(designator, properties);
if (success) {
savedComponents++;
// Получаем ID добавленного компонента для связи с вариантами
ComponentData componentData = m_database->getComponent(designator);
if (componentData.id != -1) {
// Добавляем компонент во все варианты пакетно
m_database->addComponentVariantsBatch(componentData.id, data.variantNames);
}
}
}
}
// Завершаем транзакцию
if (!m_database->commitTransaction()) {
m_database->rollbackTransaction();
}
}
// Сохранение вариативных свойств
void AltiumParser::saveVariantProperties(const ProjectData &data)
{
for (int variantIndex = 0; variantIndex < data.componentsVariantList.size(); variantIndex++) {
const QList<QList<ComponentProperty>> &variantComponents = data.componentsVariantList[variantIndex];
// Получаем название варианта (пропускаем "No Variations")
if (variantIndex + 1 >= data.variantNames.size()) {
continue;
}
QString variantName = data.variantNames[variantIndex + 1];
int variantId = m_database->getVariantId(variantName);
if (variantId == -1) {
continue;
}
for (const QList<ComponentProperty> &componentProps : variantComponents) {
QString designator;
QMap<QString, QString> properties;
for (const ComponentProperty &prop : componentProps) {
if (prop.name == "Designator") {
designator = prop.text;
} else {
properties[prop.name] = prop.text;
}
}
if (!designator.isEmpty()) {
// Получаем ID компонента
ComponentData componentData = m_database->getComponent(designator);
int componentId = componentData.id;
if (componentId != -1) {
// Добавляем вариативные свойства
for (auto it = properties.begin(); it != properties.end(); ++it) {
m_database->setVariantProperty(componentId, variantId, it.key(), it.value());
}
// Устанавливаем статус DNF/Fitted
bool isDnf = false;
if (variantIndex < data.dnfVariantDesignatorsList.size()) {
isDnf = data.dnfVariantDesignatorsList[variantIndex].contains(designator);
}
m_database->setComponentVariantFitted(componentId, variantId, !isDnf);
}
}
}
}
}
// Сохранение параметров проекта
void AltiumParser::saveProjectParameters(const ProjectData &data)
{
if (!m_database) return;
// Создаем плоский список всех свойств компонентов для обработки сложных строк
QList<ComponentProperty> allComponentProps;
for (const QList<ComponentProperty> &componentProps : data.componentsList) {
allComponentProps.append(componentProps);
}
for (int variantIndex = 0; variantIndex < data.prjParamsVariantList.size(); variantIndex++) {
const QList<QStringList> &variantParams = data.prjParamsVariantList[variantIndex];
QString variantName = (variantIndex < data.variantNames.size()) ? data.variantNames[variantIndex] : "No Variations";
for (const QStringList &param : variantParams) {
if (param.size() >= 2) {
QString name = param[0];
QString value = param[1];
// Обработка сложных строк вида ='prop1'+'prop2' или ="prop1"+"prop2"
value = makeComplexStringIfItIs(value, allComponentProps);
m_database->setProjectParam(name, value);
}
}
}
}
// Сохранение данных PCB в базу данных
void AltiumParser::savePCBDataToDatabase(const ProjectData &data)
{
if (!m_database) {
return;
}
// Создаем структуру PCBData
PCBData pcbData;
pcbData.id = -1; // Новая запись
pcbData.layerCount = data.pcbLayerCount;
pcbData.dielMaterials = data.pcbDielMaterials;
// Сохраняем данные PCB
m_database->savePCBData(pcbData);
}
// Установка данных в модель
void AltiumParser::setupModelData(const ProjectData &data, ComponentModel *model)
{
// Устанавливаем данные в модель
m_componentsList = data.componentsList;
m_variantNamesList = data.variantNames;
// Создаем варианты
m_variantsList.clear();
for (const QString &variantName : data.variantNames) {
VariantInfo variant;
variant.name = variantName;
variant.dnfDesignators = QStringList();
variant.fittedDesignators = QStringList();
variant.componentVariants = QList<QList<ComponentProperty>>();
variant.parameters = QList<QMap<QString, QString>>();
m_variantsList.append(variant);
}
}
QString AltiumParser::findParamDesignator(QTextStream &reader, const QString &paramNumber)
{
qint64 currentPos = reader.pos();
QString designator;
// Читаем следующие строки для поиска ParamDesignator
for (int i = 0; i < 10; i++) {
QString line = reader.readLine().trimmed();
if (line.startsWith("ParamDesignator" + paramNumber + "=")) {
designator = line.mid(15 + paramNumber.length() + 1);
break;
}
}
// Возвращаемся к исходной позиции
reader.seek(currentPos);
return designator;
}
QString AltiumParser::makeComplexStringIfItIs(const QString &text, const QList<ComponentProperty> &componentProps)
{
if (text.isEmpty() || text[0] != '=') {
return text;
}
QString result = text.mid(1); // Убираем '='
// Обрабатываем сложные строки вида ='prop1'+'prop2' или ="prop1"+"prop2"
QRegularExpression regex(R"(['"]([^'"]+)['"])");
QRegularExpressionMatchIterator it = regex.globalMatch(result);
while (it.hasNext()) {
QRegularExpressionMatch match = it.next();
QString propName = match.captured(1);
// Ищем значение свойства
QString propValue;
for (const ComponentProperty &prop : componentProps) {
if (prop.name == propName) {
propValue = prop.text;
break;
}
}
// Заменяем в результате
result.replace(match.captured(0), propValue);
}
return result;
}
QString AltiumParser::makeDesignatorForOrdering(const QString &designator)
{
// Извлекаем буквенную и числовую части
QRegularExpression regex(R"(([A-Za-z]+)(\d+))");
QRegularExpressionMatch match = regex.match(designator);
if (match.hasMatch()) {
QString letters = match.captured(1);
QString numbers = match.captured(2);
// Форматируем для правильной сортировки
return letters.toUpper() + numbers.rightJustified(5, '0');
}
return designator.toUpper();
}
QString AltiumParser::extractComponentDesignation(const QString &line)
{
Q_UNUSED(line)
// TODO: Реализовать извлечение обозначения компонента
return QString();
}
QString AltiumParser::extractComponentName(const QString &line)
{
Q_UNUSED(line)
// TODO: Реализовать извлечение наименования компонента
return QString();
}
QString AltiumParser::extractComponentValue(const QString &line)
{
Q_UNUSED(line)
// TODO: Реализовать извлечение номинала компонента
return QString();
}
QString AltiumParser::extractComponentFootprint(const QString &line)
{
Q_UNUSED(line)
// TODO: Реализовать извлечение корпуса компонента
return QString();
}
QString AltiumParser::extractComponentManufacturer(const QString &line)
{
Q_UNUSED(line)
// TODO: Реализовать извлечение производителя компонента
return QString();
}
QString AltiumParser::extractComponentPartNumber(const QString &line)
{
Q_UNUSED(line)
// TODO: Реализовать извлечение партномера компонента
return QString();
}
int AltiumParser::extractComponentQuantity(const QString &line)
{
Q_UNUSED(line)
// TODO: Реализовать извлечение количества компонента
return 1;
}
QString AltiumParser::lastError() const
{
return m_lastError;
}
void AltiumParser::clearError()
{
m_lastError.clear();
}
QString AltiumParser::getPcbDocFileName() const
{
return m_pcbDocFileName;
}
void AltiumParser::initializeRegex()
{
// Инициализация регулярных выражений для парсинга
m_designationRegex = QRegularExpression(R"(([A-Z][0-9]+))");
m_nameRegex = QRegularExpression(R"(([A-Za-zА-Яа-я\s]+))");
m_valueRegex = QRegularExpression(R"(([0-9]+[A-Za-z]*))");
m_footprintRegex = QRegularExpression(R"(([A-Za-z0-9_-]+))");
m_manufacturerRegex = QRegularExpression(R"(([A-Za-zА-Яа-я\s]+))");
m_partNumberRegex = QRegularExpression(R"(([A-Za-z0-9_-]+))");
m_quantityRegex = QRegularExpression(R"(([0-9]+))");
}
bool AltiumParser::parseSchLine(const QString &line, QMap<QString, QString> &component)
{
Q_UNUSED(line)
Q_UNUSED(component)
// TODO: Реализовать парсинг строки схемы
return false;
}
bool AltiumParser::parsePcbLine(const QString &line, QMap<QString, QString> &component)
{
Q_UNUSED(line)
Q_UNUSED(component)
// TODO: Реализовать парсинг строки печатной платы
return false;
}
bool AltiumParser::parseBomLine(const QString &line, QMap<QString, QString> &component)
{
Q_UNUSED(line)
Q_UNUSED(component)
// TODO: Реализовать парсинг строки BOM
return false;
}
QString AltiumParser::cleanString(const QString &str)
{
return str.trimmed();
}
bool AltiumParser::isValidComponent(const QMap<QString, QString> &component)
{
Q_UNUSED(component)
// TODO: Реализовать валидацию компонента
return true;
}
void AltiumParser::setError(const QString &error)
{
m_lastError = error;
emit parsingError(error);
}
DataBaseController* AltiumParser::getDatabase() const
{
return m_database;
}
void AltiumParser::setDatabase(DataBaseController *database)
{
m_database = database;
}
// Асинхронный парсинг
bool AltiumParser::parseFileAsync(const QString &filename, ComponentModel *model)
{
if (m_isParsingAsync) {
return false;
}
m_isParsingAsync = true;
emit parsingStarted();
emit parsingProgress(0);
// Запускаем парсинг в отдельном потоке
QFuture<bool> future = QtConcurrent::run([this, filename, model]() {
return parsePrjPcbFile(filename, model);
});
m_parseWatcher->setFuture(future);
return true;
}
bool AltiumParser::isParsingAsync() const
{
return m_isParsingAsync;
}
void AltiumParser::cancelParsing()
{
if (m_isParsingAsync && m_parseWatcher->isRunning()) {
m_parseWatcher->cancel();
m_isParsingAsync = false;
}
}
void AltiumParser::onParseFinished()
{
bool success = m_parseWatcher->result();
m_isParsingAsync = false;
if (success) {
emit parsingProgress(100);
emit parsingFinished(true);
} else {
emit parsingFinished(false);
emit parsingError(m_lastError);
}
}
void AltiumParser::onParseProgress(int progress)
{
emit parsingProgress(progress);
}
bool AltiumParser::parseSchDocFile(const QString &filename, QList<QList<ComponentProperty>> &componentsList)
{
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return false;
}
QTextStream reader(&file);
reader.setCodec(QTextCodec::codecForName("Windows-1251"));
QList<ComponentProperty> componentPropList;
QList<ComponentProperty> otherPropList;
bool isNoBom = false;
bool isFirstPartOfComponent = true;
bool isComponent = false;
int numberOfStrings = 0;
while (!reader.atEnd()) {
QString schStr = reader.readLine().trimmed();
QStringList schStrArray = schStr.split('|');
for (int i = 0; i < schStrArray.size() - 1; i++) {
if (isComponent == true) {
if (schStrArray[i].length() >= 23) {
if (schStrArray[i].left(23).toUpper() == "COMPONENTKINDVERSION2=5") {
isNoBom = true;
}
}
if (schStrArray[i].length() >= 15) {
if (schStrArray[i].left(14).toUpper() == "CURRENTPARTID=") {
if (schStrArray[i].left(15).toUpper() != "CURRENTPARTID=1" || schStrArray[i].length() > 15) {
isFirstPartOfComponent = false;
}
}
}
if (schStrArray[i].length() > 5 && schStrArray[i + 1].length() > 5) {
if (schStrArray[i].left(5).toUpper() == "TEXT=" && schStrArray[i + 1].left(5).toUpper() == "NAME=") {
ComponentProperty prop;
prop.name = schStrArray[i + 1].mid(5);
prop.text = schStrArray[i].mid(5);
if (isNoBom == false) {
componentPropList.append(prop);
}
}
}
if (schStrArray[i].length() >= 8) {
if (schStrArray[i].left(7).toUpper() == "HEADER=" || (schStrArray[i].toUpper() == "RECORD=1" && schStrArray[i].length() == 8)) {
if (isNoBom == false && componentPropList.size() > 0 && isFirstPartOfComponent == true) {
componentsList.append(componentPropList);
numberOfStrings++;
}
isNoBom = false;
isComponent = false;
isFirstPartOfComponent = true;
componentPropList.clear();
}
}
} else if (isComponent == false) {
// Запись полей основной надписи в базу данных проекта
if (schStrArray[i].length() > 5 && schStrArray[i + 1].length() > 5) {
if (schStrArray[i].left(5).toUpper() == "TEXT=" && schStrArray[i + 1].left(5).toUpper() == "NAME=") {
ComponentProperty prop;
prop.name = schStrArray[i + 1].mid(5);
prop.text = schStrArray[i].mid(5);
otherPropList.append(prop);
}
}
}
// Ищем все записи компонента
if (schStrArray[i].length() == 8) {
if (schStrArray[i].left(8).toUpper() == "RECORD=1") {
isComponent = true;
}
}
}
}
// Работа с составными именами типа ='prop1'+'prop2'
for (int i = 0; i < otherPropList.size(); i++) {
otherPropList[i].text = makeComplexStringIfItIs(otherPropList[i].text, otherPropList);
}
qDebug() << "AltiumParser: Парсинг схемы завершен, найдено компонентов:" << numberOfStrings;
file.close();
return true;
}
bool AltiumParser::parsePcbDocFile(const QString &filename, QList<DielProperty> &dielMaterials, int &pcbLayersCount)
{
qDebug() << "AltiumParser PCB: Начинаем парсинг файла платы:" << filename;
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "AltiumParser PCB: Не удалось открыть файл платы:" << filename;
return false;
}
pcbLayersCount = 0;
dielMaterials.clear();
QTextStream in(&file);
in.setCodec(QTextCodec::codecForName("Windows-1251"));
qDebug() << "AltiumParser PCB: Начинаем чтение файла построчно...";
// Переменные для поиска LAYERSET
QString signalLayersetNumber;
QString layersetLayers;
while (!in.atEnd()) {
QString line = in.readLine();
QStringList parts = line.split('|');
for (int i = 0; i < parts.size() - 1; i++) {
QString part = parts[i];
// Ищем LAYERSETxNAME=&Signal Layers
if (part.startsWith("LAYERSET") && part.contains("NAME=&Signal Layers")) {
QRegularExpression layersetNameRegex(R"(LAYERSET(\d+)NAME=&Signal Layers)");
QRegularExpressionMatch match = layersetNameRegex.match(part);
if (match.hasMatch()) {
signalLayersetNumber = match.captured(1);
}
}
// Ищем соответствующий LAYERSETxLAYERS=
if (!signalLayersetNumber.isEmpty() && part.startsWith(QString("LAYERSET%1LAYERS=").arg(signalLayersetNumber))) {
layersetLayers = part.mid(QString("LAYERSET%1LAYERS=").arg(signalLayersetNumber).length());
// Сбрасываем счетчик слоев перед подсчетом
pcbLayersCount = 0;
// Подсчитываем слои, исключая MultiLayer
QStringList layers = layersetLayers.split(',');
for (const QString &layer : layers) {
if (layer.trimmed() != "MultiLayer") {
pcbLayersCount++;
}
}
}
// Ищем записи V9_STACK_LAYER для подсчета слоев (старый метод как fallback)
if (part.startsWith("V9_STACK_LAYER") && part.contains("_NAME=")) {
// Извлекаем номер слоя из записи V9_STACK_LAYER{номер}_NAME=
QRegularExpression layerRegex(R"(V9_STACK_LAYER(\d+)_NAME=)");
QRegularExpressionMatch match = layerRegex.match(part);
if (match.hasMatch()) {
int layerNumber = match.captured(1).toInt();
if (layerNumber > pcbLayersCount) {
pcbLayersCount = layerNumber + 1; // +1 потому что нумерация с 0
}
}
}
// Ищем записи V9_STACK_LAYER с диэлектрическими свойствами
if (part.startsWith("V9_STACK_LAYER") && part.contains("_DIELTYPE=")) {
// Извлекаем номер слоя и тип диэлектрика
QRegularExpression dielTypeRegex(R"(V9_STACK_LAYER(\d+)_DIELTYPE=(\d+))");
QRegularExpressionMatch match = dielTypeRegex.match(part);
if (match.hasMatch()) {
QString layerNumber = match.captured(1);
int dielType = match.captured(2).toInt();
// Создаем новый материал
DielProperty material;
material.dielType = dielType;
material.layerNumber = layerNumber.toInt();
// Ищем толщину для этого слоя
QString heightKey = QString("V9_STACK_LAYER%1_DIELHEIGHT=").arg(layerNumber);
QString materialKey = QString("V9_STACK_LAYER%1_DIELMATERIAL=").arg(layerNumber);
// Ищем в текущей строке и следующих частях
for (int j = i; j < parts.size(); j++) {
QString searchPart = parts[j];
// Ищем толщину
if (searchPart.startsWith(heightKey)) {
QString heightStr = searchPart.mid(heightKey.length());
if (heightStr.endsWith("mil")) {
heightStr.chop(3);
}
bool ok;
double height = heightStr.toDouble(&ok);
if (ok) {
material.height = height * 0.0254; // Конвертация в мм
}
}
// Ищем материал
if (searchPart.startsWith(materialKey)) {
material.name = "DielMaterial";
material.value = searchPart.mid(materialKey.length());
// Добавляем материал в список, если у нас есть все данные
if (!material.value.isEmpty() && material.height > 0) {
dielMaterials.append(material);
}
break;
}
}
}
}
// Также ищем записи LAYER_V8_ для совместимости со старым форматом
if (part.startsWith("LAYER_V8_") && part.contains("_DIELTYPE=")) {
QRegularExpression layerV8Regex(R"(LAYER_V8_(\d+)_DIELTYPE=(\d+))");
QRegularExpressionMatch match = layerV8Regex.match(part);
if (match.hasMatch()) {
QString layerNumber = match.captured(1);
int dielType = match.captured(2).toInt();
DielProperty material;
material.dielType = dielType;
QString heightKey = QString("LAYER_V8_%1_DIELHEIGHT=").arg(layerNumber);
QString materialKey = QString("LAYER_V8_%1_DIELMATERIAL=").arg(layerNumber);
for (int j = i; j < parts.size(); j++) {
QString searchPart = parts[j];
if (searchPart.startsWith(heightKey)) {
QString heightStr = searchPart.mid(heightKey.length());
if (heightStr.endsWith("mil")) {
heightStr.chop(3);
}
bool ok;
double height = heightStr.toDouble(&ok);
if (ok) {
material.height = height * 0.0254;
}
}
if (searchPart.startsWith(materialKey)) {
material.name = "DielMaterial";
material.value = searchPart.mid(materialKey.length());
if (!material.value.isEmpty() && material.height > 0) {
dielMaterials.append(material);
}
break;
}
}
}
}
}
}
file.close();
// Если не нашли LAYERSET, используем старый метод подсчета
if (signalLayersetNumber.isEmpty()) {
qDebug() << "AltiumParser PCB: LAYERSET не найден, используем старый метод подсчета слоев";
// Убираем слой MultiLayer если он есть (для старого метода)
if (pcbLayersCount > 0) {
pcbLayersCount--;
}
}
// Итоговое логирование результатов парсинга
qDebug() << "AltiumParser PCB: === РЕЗУЛЬТАТЫ ПАРСИНГА PCB ===";
qDebug() << "AltiumParser PCB: Количество слоев:" << pcbLayersCount;
qDebug() << "AltiumParser PCB: Количество материалов:" << dielMaterials.size();
// Выводим найденные материалы
for (int i = 0; i < dielMaterials.size(); i++) {
const DielProperty &material = dielMaterials[i];
QString typeStr = (material.dielType == 1) ? "ядро" : (material.dielType == 2) ? "препрег" : "неизвестно";
qDebug() << "AltiumParser PCB: Материал" << i << ":" << material.name << "=" << material.value
<< "тип:" << typeStr << "толщина:" << material.height << "мм";
}
if (pcbLayersCount == 0) {
qDebug() << "AltiumParser PCB: ПРЕДУПРЕЖДЕНИЕ: Слои не найдены в файле!";
}
if (dielMaterials.isEmpty()) {
qDebug() << "AltiumParser PCB: ПРЕДУПРЕЖДЕНИЕ: Материалы не найдены в файле!";
}
qDebug() << "AltiumParser PCB: Парсинг платы завершен";
return true;
}