diff --git a/sc-tools/sc-builder/src/sc_builder_runner.cpp b/sc-tools/sc-builder/src/sc_builder_runner.cpp index 6cecfaaea..4196bda62 100644 --- a/sc-tools/sc-builder/src/sc_builder_runner.cpp +++ b/sc-tools/sc-builder/src/sc_builder_runner.cpp @@ -7,12 +7,14 @@ #include "sc_builder_runner.hpp" #include +#include #include #include #include #include "sc-builder/builder.hpp" +#include "gwf_translator.hpp" void PrintHelpMessage(std::string const & binaryName) { @@ -44,7 +46,12 @@ void PrintHelpMessage(std::string const & binaryName) << " --clear Run sc-builder in a mode that overwrites existing knowledge base " "binaries.\n" << " --version Display the version of " << binaryName << ".\n" - << " --help Display this help message.\n"; + << " --help Display this help message.\n" + << " --gwf-to-scs Translate a GWF file to SCs and save the result to a specified " + "file.\n" + << " --gwf-output Specify the output file for the GWF-to-SCs translation (default: " + ".scs).\n" + << " --verbose|-v Enable verbose logging.\n"; } sc_int RunBuilder(sc_int argc, sc_char * argv[]) @@ -65,6 +72,36 @@ try return EXIT_SUCCESS; } + if (options.Has({"gwf-to-scs"})) + { + std::string gwfFile = options[{"gwf-to-scs"}].second; + std::string outputFile = options.Has({"gwf-output"}) ? options[{"gwf-output"}].second : gwfFile + ".scs"; + + // Инициализация контекста для транслятора + ScMemoryContext ctx; + GWFTranslator translator(ctx); + std::string scsText = translator.TranslateXMLFileContentToSCs(gwfFile); + + // Сохранение результата в файл + std::ofstream output(outputFile, std::ios::binary); + if (!output.is_open()) + { + std::cout << "Error: Cannot open output file `" << outputFile << "`.\n"; + return EXIT_FAILURE; + } + output << scsText; + output.close(); + + if (output.fail()) + { + std::cout << "Error: Failed to write to output file `" << outputFile << "`.\n"; + return EXIT_FAILURE; + } + + std::cout << "GWF file `" << gwfFile << "` successfully translated to SCs and saved as `" << outputFile << "`.\n"; + return EXIT_SUCCESS; + } + BuilderParams params; if (options.Has({"input", "i"})) params.m_inputPath = options[{"input", "i"}].second; @@ -155,8 +192,10 @@ try "[sc-builder] group.\n" << "Make sure this path is a valid directory where you have write permissions and that it exists.\n" << "For more information, run with --help.\n"; + return EXIT_FAILURE; } + params.m_resultStructureUpload = formedMemoryParams.init_memory_generated_upload; if (formedMemoryParams.init_memory_generated_structure != nullptr) params.m_resultStructureSystemIdtf = formedMemoryParams.init_memory_generated_structure; diff --git a/sc-tools/sc-builder/src/sc_scs_writer.cpp b/sc-tools/sc-builder/src/sc_scs_writer.cpp index 8fdde8367..e98bab2ff 100644 --- a/sc-tools/sc-builder/src/sc_scs_writer.cpp +++ b/sc-tools/sc-builder/src/sc_scs_writer.cpp @@ -7,11 +7,15 @@ #include "sc_scs_writer.hpp" #include +#include +#include +#include #include #include "sc_scg_element.hpp" #include "sc_scs_element.hpp" +#include "sc_scg_to_scs_types_converter.hpp" using namespace Constants; @@ -92,6 +96,27 @@ void SCsWriter::SCgIdentifierCorrector::GenerateSCsIdentifier( scsElement->SetIdentifierForSCs(SCsWriter::MakeAlias(CONNECTOR, id)); } +// Вспомогательная функция для рекурсивного сбора узлов +void SCsWriter::CollectNodes( + SCgElements const & elements, + std::unordered_set & nodes, + std::unordered_set & visitedContours) +{ + for (auto const & [id, element] : elements) + { + if (element->GetTag() == NODE) + { + nodes.insert(element); + } + else if (element->GetTag() == CONTOUR && !visitedContours.count(element)) + { + visitedContours.insert(element); + auto contour = std::dynamic_pointer_cast(element); + CollectNodes(contour->GetElements(), nodes, visitedContours); + } + } +} + void SCsWriter::Write( SCgElements const & elements, std::string const & filePath, @@ -99,74 +124,232 @@ void SCsWriter::Write( size_t depth, std::unordered_set & writtenElements) { - std::list dependedConnectors; + // Шаг 1: Сбор всех узлов, включая вложенные в контуры + std::unordered_set allNodes; + std::unordered_set visitedContours; + CollectNodes(elements, allNodes, visitedContours); - auto it = elements.cbegin(); - while (it != elements.cend()) + // Запись типов всех узлов + for (auto const & node : allNodes) { - SCgElementPtr scgElement = it->second; + if (writtenElements.count(node)) + continue; + writtenElements.insert(node); + std::string identifier = node->GetIdentifier(); + if (identifier.empty()) + identifier = NODE + UNDERSCORE + node->GetId(); + buffer.AddTabs(depth) << identifier << NEWLINE; + + // Запись типа узла + std::string elementTypeStr; + SCgToSCsTypesConverter::ConvertSCgNodeTypeToSCsNodeType(node->GetType(), elementTypeStr); + if (elementTypeStr.empty()) + elementTypeStr = NODE + UNDERSCORE; + buffer.AddTabs(depth + 1) << SC_CONNECTOR_MAIN_L << elementTypeStr << ELEMENT_END << NEWLINE; - bool isElementWritable = true; - std::string const & scgTag = scgElement->GetTag(); - if (scgTag == BUS) - isElementWritable = false; - else if (scgTag == PAIR || scgTag == ARC) + // Проверка, является ли узел SCgLink, и запись содержимого + auto link = std::dynamic_pointer_cast(node); + if (link && link->GetContentType() != NO_CONTENT) { - auto scgConnector = std::dynamic_pointer_cast(scgElement); - auto const & source = scgConnector->GetSource(); - auto const & target = scgConnector->GetTarget(); + std::string contentTypeStr = link->GetContentType(); + int contentType = std::stoi(contentTypeStr); - if (writtenElements.find(source) == writtenElements.cend() - || writtenElements.find(target) == writtenElements.cend()) + if (contentType == 1 || contentType == 2 || contentType == 3) // STRING, INTEGER, FLOAT { - dependedConnectors.push_back(scgElement); - isElementWritable = false; + std::string content = link->GetContentData(); + if (!content.empty()) + { + if (contentType == 2) // INTEGER + content = DOUBLE_QUOTE + INT64 + content + DOUBLE_QUOTE; + else if (contentType == 3) // FLOAT + content = DOUBLE_QUOTE + FLOAT + content + DOUBLE_QUOTE; + buffer.AddTabs(depth + 1) << SPACE << EQUAL << SPACE << OPEN_BRACKET << content + << CLOSE_BRACKET + ELEMENT_END + NEWLINE; + } + } + else if (contentType == 4) // IMAGE + { + std::string fileName = link->GetFileName(); + std::string content = link->GetContentData(); + if (!fileName.empty() && !content.empty()) + { + // Формирование пути к файлу + std::filesystem::path basePath = std::filesystem::path(filePath).parent_path(); + std::filesystem::path fullPath = basePath / fileName; + + // Сохранение содержимого в файл + std::ofstream file(fullPath, std::ios::binary); + if (!file) + SC_THROW_EXCEPTION( + utils::ExceptionInvalidParams, + "SCsWriter::Write: Failed to open file for writing `" << fullPath.string() << "`."); + + file.write(content.data(), content.size()); + file.close(); + + // Определение формата изображения + std::string fileExtension = fullPath.extension().string(); + auto it = IMAGE_FORMATS.find(fileExtension); + if (it == IMAGE_FORMATS.end()) + SC_THROW_EXCEPTION( + utils::ExceptionItemNotFound, + "SCsWriter::Write: File extension `" << fileExtension << "` is not supported."); + + std::string imageFormat = it->second; + std::string fileContent = FILE_PREFIX + fullPath.filename().string(); + + // Вывод SCs-кода для изображения + buffer.AddTabs(depth + 1) << EQUAL + SPACE + DOUBLE_QUOTE << fileContent + << DOUBLE_QUOTE + ELEMENT_END + NEWLINE; + buffer.AddTabs(depth + 1) << FORMAT_ARC + SPACE + EQUAL + SPACE + OPEN_PARENTHESIS << identifier + << SPACE + SC_CONNECTOR_DCOMMON_R + SPACE << imageFormat + << CLOSE_PARENTHESIS + ELEMENT_END + NEWLINE; + buffer.AddTabs(depth + 1) << NREL_FORMAT_ARC << SPACE + EQUAL + SPACE + << OPEN_PARENTHESIS + NREL_FORMAT + SPACE << SC_CONNECTOR_MAIN_R + SPACE + << FORMAT_ARC + CLOSE_PARENTHESIS + ELEMENT_END + NEWLINE; + } } } + buffer << NEWLINE; + } - if (isElementWritable) + // Шаг 2: Предварительная обработка для дуг + std::unordered_set complexArcs; + std::unordered_set attributeArcs; + for (auto const & [id, element] : elements) + { + if (element->GetTag() == ARC || element->GetTag() == PAIR) { - auto const & scsElement = SCsElementFactory::CreateSCsElementForSCgElement(scgElement); - scsElement->ConvertFromSCgElement(scgElement); - scsElement->Dump(filePath, buffer, depth, writtenElements); - writtenElements.insert(scgElement); + auto connector = std::dynamic_pointer_cast(element); + auto target = connector->GetTarget(); + if (target->GetTag() == ARC || target->GetTag() == PAIR) + { + complexArcs.insert(target); + attributeArcs.insert(element); + } } - - ++it; } - if (!dependedConnectors.empty()) + + // Шаг 3: Запись дуг и пар + for (auto const & [id, element] : elements) { - size_t connectorsWrittenOnPreviousIteration = dependedConnectors.size(); - while (connectorsWrittenOnPreviousIteration != 0) + if (element->GetTag() == ARC || element->GetTag() == PAIR) { - connectorsWrittenOnPreviousIteration = 0; - for (auto dependedConnector = dependedConnectors.cbegin(); dependedConnector != dependedConnectors.cend();) + if (writtenElements.count(element)) + continue; + if (attributeArcs.count(element)) + continue; + writtenElements.insert(element); + + auto connector = std::dynamic_pointer_cast(element); + std::string sourceId = connector->GetSource()->GetIdentifier(); + std::string targetId = connector->GetTarget()->GetIdentifier(); + if (sourceId.empty()) + sourceId = NODE + UNDERSCORE + connector->GetSource()->GetId(); + if (targetId.empty()) + targetId = NODE + UNDERSCORE + connector->GetTarget()->GetId(); + + if (complexArcs.count(element)) { - auto scgConnector = std::dynamic_pointer_cast(*dependedConnector); - auto const & source = scgConnector->GetSource(); - auto const & target = scgConnector->GetTarget(); - bool isElementWritable = - (writtenElements.find(source) != writtenElements.cend() - && writtenElements.find(target) != writtenElements.cend()); - if (isElementWritable) + std::string attrSourceId; + for (auto const & [incomingId, incomingElement] : elements) { - auto const & scsElement = SCsElementFactory::CreateSCsElementForSCgElement(*dependedConnector); - scsElement->ConvertFromSCgElement(*dependedConnector); - scsElement->Dump(filePath, buffer, depth, writtenElements); - writtenElements.insert(*dependedConnector); - ++connectorsWrittenOnPreviousIteration; - dependedConnector = dependedConnectors.erase(dependedConnector); + if (incomingElement->GetTag() == ARC || incomingElement->GetTag() == PAIR) + { + auto incConnector = std::dynamic_pointer_cast(incomingElement); + if (incConnector->GetTarget() == element) + { + attrSourceId = incConnector->GetSource()->GetIdentifier(); + if (attrSourceId.empty()) + attrSourceId = NODE + UNDERSCORE + incConnector->GetSource()->GetId(); + break; + } + } + } + + if (!attrSourceId.empty()) + { + std::string connectorSymbol; + SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation( + connector->GetType(), connectorSymbol); + if (connectorSymbol.empty()) + connectorSymbol = SC_CONNECTOR_MAIN_R; + buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << attrSourceId << COLON << SPACE + << targetId << ELEMENT_END << NEWLINE << NEWLINE; } else - ++dependedConnector; + { + std::string connectorSymbol; + SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation( + connector->GetType(), connectorSymbol); + if (connectorSymbol.empty()) + connectorSymbol = SC_CONNECTOR_MAIN_R; + buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << targetId << ELEMENT_END << NEWLINE + << NEWLINE; + } + } + else + { + std::string connectorSymbol; + SCgToSCsTypesConverter::ConvertSCgConnectorTypeToSCsConnectorDesignation(connector->GetType(), connectorSymbol); + if (connectorSymbol.empty()) + connectorSymbol = SC_CONNECTOR_MAIN_R; + buffer.AddTabs(depth) << sourceId << SPACE << connectorSymbol << SPACE << targetId << ELEMENT_END << NEWLINE + << NEWLINE; } } } - if (!dependedConnectors.empty()) - SC_THROW_EXCEPTION( - utils::ExceptionInvalidState, - "SCsWriter: unknown incident elements for " << dependedConnectors.size() << " connectors at `" << filePath - << "`."); + + // Шаг 4: Запись контуров + for (auto const & [id, element] : elements) + { + if (element->GetTag() == CONTOUR) + { + if (writtenElements.count(element)) + continue; + writtenElements.insert(element); + auto contour = std::dynamic_pointer_cast(element); + std::string identifier = element->GetIdentifier(); + if (identifier.empty()) + identifier = CONTOUR + UNDERSCORE + element->GetId(); + buffer.AddTabs(depth) << identifier << SPACE + EQUAL << SPACE << OPEN_CONTOUR + NEWLINE; + + // Сбор узлов, принадлежащих текущему контуру + std::unordered_set contourNodes; + std::unordered_set contourVisitedContours; + CollectNodes(contour->GetElements(), contourNodes, contourVisitedContours); + + // Сбор узлов, участвующих в дугах внутри контура + std::unordered_set nodesInArcs; + for (auto const & [elemId, elem] : contour->GetElements()) + { + if (elem->GetTag() == ARC || elem->GetTag() == PAIR) + { + auto connector = std::dynamic_pointer_cast(elem); + if (connector->GetSource()->GetTag() == NODE) + nodesInArcs.insert(connector->GetSource()); + if (connector->GetTarget()->GetTag() == NODE) + nodesInArcs.insert(connector->GetTarget()); + } + } + + // Запись идентификаторов узлов, не участвующих в дугах + for (auto const & node : contourNodes) + { + if (nodesInArcs.count(node)) + continue; // Пропускаем узлы, которые будут в дугах + std::string nodeIdentifier = node->GetIdentifier(); + if (nodeIdentifier.empty()) + nodeIdentifier = NODE + UNDERSCORE + node->GetId(); + buffer.AddTabs(depth + 1) << nodeIdentifier << ELEMENT_END << NEWLINE; + } + + // Рекурсивная запись элементов контура (дуги, вложенные контуры) + Write(contour->GetElements(), filePath, buffer, depth + 1, writtenElements); + + buffer.AddTabs(depth) << CONTOUR_END << NEWLINE << NEWLINE; + } + } } void SCsWriter::WriteMainIdentifier( @@ -179,4 +362,4 @@ void SCsWriter::WriteMainIdentifier( buffer.AddTabs(depth) << systemIdentifier << NEWLINE; buffer.AddTabs(depth) << SPACE << SC_CONNECTOR_DCOMMON_R << SPACE << NREL_MAIN_IDTF << COLON << SPACE << OPEN_BRACKET << mainIdentifier << CLOSE_BRACKET << ELEMENT_END << NEWLINE; -} +} \ No newline at end of file diff --git a/sc-tools/sc-builder/src/sc_scs_writer.hpp b/sc-tools/sc-builder/src/sc_scs_writer.hpp index 2e6b78f93..b3b637235 100644 --- a/sc-tools/sc-builder/src/sc_scs_writer.hpp +++ b/sc-tools/sc-builder/src/sc_scs_writer.hpp @@ -24,6 +24,15 @@ class SCsWriter size_t depth, std::unordered_set & writtenElements); + static void WriteMainIdentifier( + Buffer & buffer, + size_t depth, + std::string const & systemIdentifier, + std::string const & mainIdentifier); + + static std::string MakeAlias(std::string const & prefix, std::string const & elementId); + static bool IsVariable(std::string const & elementType); + class SCgIdentifierCorrector { public: @@ -44,11 +53,9 @@ class SCsWriter static std::string GenerateSCsIdentifierForVariable(std::string & systemIdentifier); }; - static void WriteMainIdentifier( - Buffer & buffer, - size_t depth, - std::string const & systemIdentifier, - std::string const & mainIdentifier); - static std::string MakeAlias(std::string const & prefix, std::string const & elementId); - static bool IsVariable(std::string const & elementType); -}; +private: + static void CollectNodes( + SCgElements const & elements, + std::unordered_set & nodes, + std::unordered_set & visitedContours); +}; \ No newline at end of file