394 lines
13 KiB
C++
394 lines
13 KiB
C++
//
|
|
// Created by bbr on 27.05.22..
|
|
//
|
|
|
|
#include "application.h"
|
|
#include "comdel/parser/parse_context.h"
|
|
#include "comdel/parser/parser_util.h"
|
|
#include "comdel/domain/schema_creator.h"
|
|
#include "comdel/domain/comdel_generator.h"
|
|
#include "comdel/domain/comdel_validator.h"
|
|
|
|
std::optional<domain::Library> Application::getLibrary() {
|
|
return library;
|
|
}
|
|
|
|
domain::Schema *Application::getSchema() {
|
|
return schema;
|
|
}
|
|
|
|
void Application::clear() {
|
|
if (schema != nullptr) {
|
|
delete schema;
|
|
schema = nullptr;
|
|
}
|
|
library = std::nullopt;
|
|
libraryPath = "";
|
|
}
|
|
|
|
bool Application::loadLibrary(std::string &filename, std::ostream &errorOutput) {
|
|
clear();
|
|
|
|
ParseContext parseContext;
|
|
auto libraryNode = load_library_from_file(&parseContext, filename.c_str(), errorOutput);
|
|
if (libraryNode) {
|
|
domain::SchemaCreator generator(validators);
|
|
library = generator.loadLibrary(*libraryNode);
|
|
|
|
for (auto &error: generator.getErrors()) {
|
|
parseContext.formatError(error, errorOutput, "ERROR: ");
|
|
}
|
|
|
|
if (library.has_value()) {
|
|
libraryPath = filename;
|
|
// on library load we create a new schema
|
|
schema = new domain::Schema(library.value());
|
|
} else {
|
|
errorOutput << "Failed creating library model" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
} else {
|
|
errorOutput << "Failed parsing library" << std::endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::pair<bool, std::vector<domain::ValidationError>> Application::loadSchema(std::string &filename, std::ostream &errorOutput) {
|
|
clear();
|
|
|
|
std::vector<domain::ValidationError> errors;
|
|
|
|
ParseContext parseContext;
|
|
auto schemaNode = load_schema_from_file(&parseContext, filename.c_str(), errorOutput);
|
|
|
|
if (schemaNode) {
|
|
domain::SchemaCreator generator(validators);
|
|
library = generator.loadLibrary(*schemaNode->library);
|
|
|
|
libraryPath = schemaNode->source->asString();
|
|
|
|
for (auto &error: generator.getErrors()) {
|
|
parseContext.formatError(error, errorOutput, "ERROR: ");
|
|
}
|
|
|
|
if (library) {
|
|
schema = generator.loadSchema(*schemaNode, *library);
|
|
|
|
for (auto &error: generator.getErrors()) {
|
|
parseContext.formatError(error, errorOutput, "ERROR: ");
|
|
}
|
|
|
|
if (schema == nullptr) {
|
|
clear();
|
|
return {false, errors};
|
|
}
|
|
} else {
|
|
clear();
|
|
return {false, errors};
|
|
}
|
|
|
|
} else {
|
|
errorOutput << "Failed schema library" << std::endl;
|
|
return {false, errors};
|
|
}
|
|
|
|
domain::ComdelValidator validator{validators};
|
|
|
|
domain::ValidationContext context;
|
|
context.instance = nullptr;
|
|
context.attribute = nullptr;
|
|
context.addressSpaces = {};
|
|
|
|
auto memoryReferenceValidation = validator.validateMemoryReferences(*schema, *library, context);
|
|
errors.insert(errors.end(), memoryReferenceValidation.begin(), memoryReferenceValidation.end());
|
|
|
|
auto nameValidation = validator.validateInstanceNames(*schema, *library, context);
|
|
errors.insert(errors.end(), nameValidation.begin(), nameValidation.end());
|
|
|
|
if(!errors.empty()) {
|
|
clear();
|
|
return {false, errors};
|
|
}
|
|
|
|
return {true, errors};
|
|
}
|
|
|
|
bool Application::generateSchema(std::ostringstream &output) {
|
|
if (schema == nullptr) {
|
|
return false;
|
|
}
|
|
domain::generate_schema(libraryPath, schema, output);
|
|
return true;
|
|
}
|
|
|
|
std::vector<domain::ValidationError> Application::validateSchema() {
|
|
if (schema == nullptr) {
|
|
return std::vector<domain::ValidationError>{domain::ValidationError(domain::Action::ERROR, "No schema loaded")};
|
|
}
|
|
|
|
domain::ComdelValidator validator{validators};
|
|
|
|
domain::ValidationContext context;
|
|
context.instance = nullptr;
|
|
context.attribute = nullptr;
|
|
context.addressSpaces = {};
|
|
|
|
for (auto &lib: library->getAddressSpaces()) {
|
|
context.addressSpaces.insert(std::make_pair(lib.getName(), lib));
|
|
}
|
|
auto errors = validator.validateSchema(*schema, context);
|
|
|
|
auto memoryReferenceValidation = validator.validateMemoryReferences(*schema, *library, context);
|
|
errors.insert(errors.end(), memoryReferenceValidation.begin(), memoryReferenceValidation.end());
|
|
|
|
auto nameValidation = validator.validateInstanceNames(*schema, *library, context);
|
|
errors.insert(errors.end(), nameValidation.begin(), nameValidation.end());
|
|
|
|
auto countValidation = validator.validateInstanceCount(*schema, *library, context);
|
|
errors.insert(errors.end(), countValidation.begin(), countValidation.end());
|
|
|
|
auto pinValidation = validator.validatePinConnections(*schema, *library, context);
|
|
errors.insert(errors.end(), pinValidation.begin(), pinValidation.end());
|
|
return errors;
|
|
}
|
|
|
|
std::vector<domain::ValidationError> Application::generateComdel(std::ostringstream &output) {
|
|
|
|
auto errors = validateSchema();
|
|
if (!Application::hasErrors(errors)) {
|
|
// as long as all validation errors are warning we continue with build
|
|
domain::generate_comdel(schema, library.value(), output);
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
bool Application::hasErrors(std::vector<domain::ValidationError> errors) {
|
|
for (auto &err: errors) {
|
|
if (err.type == domain::Action::ERROR) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// static instance of application
|
|
static Application *application = nullptr;
|
|
|
|
Application *Application::instance() {
|
|
if (application == nullptr) {
|
|
application = new Application();
|
|
}
|
|
return application;
|
|
}
|
|
|
|
std::shared_ptr<domain::ComponentInstance>
|
|
Application::addComponent(domain::Component component, std::vector<domain::InstanceAttribute> attributes, int x,
|
|
int y) {
|
|
std::set<std::string> names;
|
|
for (const auto &c: schema->componentInstances) {
|
|
names.insert(c->name);
|
|
}
|
|
std::string name = generateName(names, component.getInstanceName());
|
|
|
|
schema->componentInstances.push_back(
|
|
std::make_shared<domain::ComponentInstance>(name, attributes, std::make_pair(x, y), component));
|
|
return schema->componentInstances.back();
|
|
}
|
|
|
|
std::shared_ptr<domain::BusInstance> Application::addBus(domain::Bus bus, int x, int y) {
|
|
std::set<std::string> names;
|
|
for (const auto &b: schema->busInstances) {
|
|
names.insert(b->name);
|
|
}
|
|
std::string name = generateName(names, bus.getInstanceName());
|
|
|
|
schema->busInstances.push_back(std::make_shared<domain::BusInstance>(name, std::make_pair(x, y), bus));
|
|
return schema->busInstances.back();
|
|
}
|
|
|
|
|
|
std::string Application::generateName(std::set<std::string> &names, std::string instanceName) {
|
|
if (names.find(instanceName) == names.end()) {
|
|
return instanceName;
|
|
}
|
|
char buffer[4];
|
|
for (int i = 0; i < 1000; i++) {
|
|
sprintf(buffer, "%03d", i);
|
|
auto name = instanceName + "_" + buffer;
|
|
if (names.find(name) == names.end()) {
|
|
return name;
|
|
}
|
|
}
|
|
// return default value as this should never happen
|
|
return instanceName;
|
|
}
|
|
|
|
bool Application::removeComponent(std::string componentName) {
|
|
auto component = findComponentByName(componentName);
|
|
if (component == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
std::set<domain::BusInstance*> automaticBusses;
|
|
|
|
schema->connections.erase(
|
|
std::remove_if(
|
|
schema->connections.begin(),
|
|
schema->connections.end(),
|
|
[component, &automaticBusses](const std::shared_ptr<domain::ConnectionInstance> &conn) {
|
|
auto busConnection = dynamic_cast<domain::BusConnectionInstance *>(conn.get());
|
|
if (busConnection) {
|
|
if (busConnection->instance == component) {
|
|
return true;
|
|
}
|
|
}
|
|
auto directConnection = dynamic_cast<domain::DirectConnectionInstance *>(conn.get());
|
|
if (directConnection) {
|
|
if (directConnection->instance == component || directConnection->secondInstance == component) {
|
|
automaticBusses.insert(directConnection->bus);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}),
|
|
schema->connections.end()
|
|
);
|
|
|
|
schema->busInstances.erase(
|
|
std::remove_if(
|
|
schema->busInstances.begin(),
|
|
schema->busInstances.end(),
|
|
[&automaticBusses](const std::shared_ptr<domain::BusInstance> &bus) {
|
|
return automaticBusses.count(bus.get());
|
|
}),
|
|
schema->busInstances.end()
|
|
);
|
|
|
|
if(component->component.getType() == domain::Component::MEMORY) {
|
|
for(auto& comp: schema->componentInstances) {
|
|
if(comp->component.getType() == domain::Component::PROCESSOR) {
|
|
for(auto& attribute: comp->attributes) {
|
|
if(attribute.value.isType(domain::Value::MEMORY_REFERENCE) && attribute.value.asMemoryReference() == component->name) {
|
|
attribute.value = domain::Value::fromMemoryReference(std::nullopt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for(auto iter = schema->componentInstances.begin(); iter != schema->componentInstances.end(); ++iter) {
|
|
if(iter->get() == component) {
|
|
schema->componentInstances.erase(iter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Application::removeConnection(domain::ConnectionInstance *connectionInstance) {
|
|
if(auto directConnection = dynamic_cast<domain::DirectConnectionInstance*>(connectionInstance)) {
|
|
schema->busInstances.erase(
|
|
std::remove_if(
|
|
schema->busInstances.begin(),
|
|
schema->busInstances.end(),
|
|
[directConnection](const std::shared_ptr<domain::BusInstance> &bus) {
|
|
return directConnection->bus == bus.get();
|
|
}),
|
|
schema->busInstances.end()
|
|
);
|
|
}
|
|
schema->connections.erase(
|
|
std::remove_if(
|
|
schema->connections.begin(),
|
|
schema->connections.end(),
|
|
[connectionInstance](const std::shared_ptr<domain::ConnectionInstance> &conn) {
|
|
return connectionInstance == conn.get();
|
|
}),
|
|
schema->connections.end()
|
|
);
|
|
|
|
}
|
|
|
|
bool Application::removeBus(std::string busName) {
|
|
auto bus = findBusByName(busName);
|
|
if (bus == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
schema->connections.erase(
|
|
std::remove_if(
|
|
schema->connections.begin(),
|
|
schema->connections.end(),
|
|
[bus](const std::shared_ptr<domain::ConnectionInstance> &conn) {
|
|
auto busConnection = dynamic_cast<domain::BusConnectionInstance *>(conn.get());
|
|
if (busConnection) {
|
|
if (busConnection->bus == bus) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}),
|
|
schema->connections.end()
|
|
);
|
|
|
|
for(auto iter = schema->busInstances.begin(); iter != schema->busInstances.end(); ++iter) {
|
|
if(iter->get() == bus) {
|
|
schema->busInstances.erase(iter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
domain::ComponentInstance *Application::findComponentByName(std::string componentName) {
|
|
for (auto &comp: schema->componentInstances) {
|
|
if (comp->name == componentName) {
|
|
return comp.get();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
domain::BusInstance *Application::findBusByName(std::string busName) {
|
|
for (auto &bus: schema->busInstances) {
|
|
if (bus->name == busName) {
|
|
return bus.get();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void Application::renameComponent(std::string currentName, std::string newName) {
|
|
if(currentName == newName) {
|
|
return;
|
|
}
|
|
auto component = findComponentByName(currentName);
|
|
if(component) {
|
|
component->name = newName;
|
|
if(component->component.getType() == domain::Component::MEMORY) {
|
|
for(auto& comp: schema->componentInstances) {
|
|
if(comp.get()->component.getType() == domain::Component::PROCESSOR) {
|
|
for(auto& attribute: comp.get()->attributes) {
|
|
if(attribute.value.isType(domain::Value::MEMORY_REFERENCE) && attribute.value.asMemoryReference() == currentName) {
|
|
attribute.value = domain::Value::fromMemoryReference(newName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Application::renameBus(std::string currentName, std::string newName) {
|
|
if(currentName == newName) {
|
|
return;
|
|
}
|
|
auto bus = findBusByName(currentName);
|
|
if(bus) {
|
|
bus->name = newName;
|
|
}
|
|
} |