diff --git a/src/Application.cpp b/src/Application.cpp index a8f037e7..800acb14 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -11,435 +11,448 @@ #include "src/Helpers/Paths.hpp" #include "Exceptions/InvalidConfig.hpp" -using namespace Bloom; -using namespace Bloom::Events; -using namespace Bloom::Exceptions; +namespace Bloom +{ + using namespace Exceptions; -int Application::run(const std::vector& arguments) { - try { - this->setName("Bloom"); + int Application::run(const std::vector& arguments) { + try { + this->setName("Bloom"); - if (!arguments.empty()) { - auto firstArg = arguments.front(); - auto commandsToCallbackMapping = this->getCommandToHandlerMapping(); + if (!arguments.empty()) { + auto firstArg = arguments.front(); + auto commandsToCallbackMapping = this->getCommandToHandlerMapping(); - if (commandsToCallbackMapping.contains(firstArg)) { - // User has passed an argument that maps to a command callback - invoke the callback and shutdown - auto returnValue = commandsToCallbackMapping.at(firstArg)(); + if (commandsToCallbackMapping.contains(firstArg)) { + // User has passed an argument that maps to a command callback - invoke the callback and shutdown + auto returnValue = commandsToCallbackMapping.at(firstArg)(); + + this->shutdown(); + return returnValue; + } + + // If the first argument didn't map to a command, we assume it's an environment name + this->selectedEnvironmentName = firstArg; + } + +#ifdef BLOOM_DEBUG_BUILD + Logger::warning("This is a debug build - some functions may not work as expected."); +#endif + + this->startup(); + + if (this->insightConfig->insightEnabled) { + this->insight = std::make_unique( + this->eventManager, + this->projectConfig.value(), + this->environmentConfig.value(), + this->insightConfig.value(), + this->projectSettings.value().insightSettings + ); + + /* + * Before letting Insight occupy the main thread, process any pending events that accumulated + * during startup. + */ + this->applicationEventListener->dispatchCurrentEvents(); + + if (Thread::getThreadState() == ThreadState::READY) { + this->insight->run(); + Logger::debug("Insight closed"); + } this->shutdown(); - return returnValue; + return EXIT_SUCCESS; } - // If the first argument didn't map to a command, we assume it's an environment name - this->selectedEnvironmentName = firstArg; - } - -#ifdef BLOOM_DEBUG_BUILD - Logger::warning("This is a debug build - some functions may not work as expected."); -#endif - - this->startup(); - - if (this->insightConfig->insightEnabled) { - this->insight = std::make_unique( - this->eventManager, - this->projectConfig.value(), - this->environmentConfig.value(), - this->insightConfig.value(), - this->projectSettings.value().insightSettings - ); - - /* - * Before letting Insight occupy the main thread, process any pending events that accumulated - * during startup. - */ - this->applicationEventListener->dispatchCurrentEvents(); - - if (Thread::getThreadState() == ThreadState::READY) { - this->insight->run(); - Logger::debug("Insight closed"); + // Main event loop + while (Thread::getThreadState() == ThreadState::READY) { + this->applicationEventListener->waitAndDispatch(); } - this->shutdown(); - return EXIT_SUCCESS; + } + catch (const InvalidConfig& exception) { + Logger::error(exception.getMessage()); + + } + catch (const Exception& exception) { + Logger::error(exception.getMessage()); } - // Main event loop - while (Thread::getThreadState() == ThreadState::READY) { - this->applicationEventListener->waitAndDispatch(); + this->shutdown(); + return EXIT_SUCCESS; + } + + bool Application::isRunningAsRoot() { + return geteuid() == 0; + } + + void Application::startup() { + auto applicationEventListener = this->applicationEventListener; + this->eventManager.registerListener(applicationEventListener); + applicationEventListener->registerCallbackForEventType( + std::bind(&Application::onShutdownApplicationRequest, this, std::placeholders::_1) + ); + + this->loadProjectSettings(); + this->loadProjectConfiguration(); + Logger::configure(this->projectConfig.value()); + + Logger::debug("Bloom version: " + Application::VERSION.toString()); + + this->blockAllSignalsOnCurrentThread(); + this->startSignalHandler(); + + Logger::info("Selected environment: \"" + this->selectedEnvironmentName + "\""); + Logger::debug("Number of environments extracted from config: " + + std::to_string(this->projectConfig->environments.size())); + + applicationEventListener->registerCallbackForEventType( + std::bind(&Application::onTargetControllerThreadStateChanged, this, std::placeholders::_1) + ); + + applicationEventListener->registerCallbackForEventType( + std::bind(&Application::onDebugServerThreadStateChanged, this, std::placeholders::_1) + ); + + this->startTargetController(); + this->startDebugServer(); + + Thread::setThreadState(ThreadState::READY); + } + + void Application::shutdown() { + auto appState = Thread::getThreadState(); + if (appState == ThreadState::STOPPED || appState == ThreadState::SHUTDOWN_INITIATED) { + return; } - } catch (const InvalidConfig& exception) { - Logger::error(exception.getMessage()); + Thread::setThreadState(ThreadState::SHUTDOWN_INITIATED); + Logger::info("Shutting down Bloom"); - } catch (const Exception& exception) { - Logger::error(exception.getMessage()); + this->stopDebugServer(); + this->stopTargetController(); + this->stopSignalHandler(); + + this->saveProjectSettings(); + Thread::setThreadState(ThreadState::STOPPED); } - this->shutdown(); - return EXIT_SUCCESS; -} + void Application::loadProjectSettings() { + const auto projectSettingsPath = Paths::projectSettingsPath(); + auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath)); -bool Application::isRunningAsRoot() { - return geteuid() == 0; -} + if (jsonSettingsFile.exists()) { + try { + if (!jsonSettingsFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + throw Exception("Failed to open settings file."); + } -void Application::startup() { - auto applicationEventListener = this->applicationEventListener; - this->eventManager.registerListener(applicationEventListener); - applicationEventListener->registerCallbackForEventType( - std::bind(&Application::onShutdownApplicationRequest, this, std::placeholders::_1) - ); + const auto jsonObject = QJsonDocument::fromJson(jsonSettingsFile.readAll()).object(); + jsonSettingsFile.close(); - this->loadProjectSettings(); - this->loadProjectConfiguration(); - Logger::configure(this->projectConfig.value()); + this->projectSettings = ProjectSettings(jsonObject); + return; - Logger::debug("Bloom version: " + Application::VERSION.toString()); + } + catch (const std::exception& exception) { + Logger::error( + "Failed to load project settings from " + projectSettingsPath + " - " + exception.what() + ); + } + } - this->blockAllSignalsOnCurrentThread(); - this->startSignalHandler(); - - Logger::info("Selected environment: \"" + this->selectedEnvironmentName + "\""); - Logger::debug("Number of environments extracted from config: " - + std::to_string(this->projectConfig->environments.size())); - - applicationEventListener->registerCallbackForEventType( - std::bind(&Application::onTargetControllerThreadStateChanged, this, std::placeholders::_1) - ); - - applicationEventListener->registerCallbackForEventType( - std::bind(&Application::onDebugServerThreadStateChanged, this, std::placeholders::_1) - ); - - this->startTargetController(); - this->startDebugServer(); - - Thread::setThreadState(ThreadState::READY); -} - -void Application::shutdown() { - auto appState = Thread::getThreadState(); - if (appState == ThreadState::STOPPED || appState == ThreadState::SHUTDOWN_INITIATED) { - return; + this->projectSettings = ProjectSettings(); } - Thread::setThreadState(ThreadState::SHUTDOWN_INITIATED); - Logger::info("Shutting down Bloom"); + void Application::saveProjectSettings() { + if (!this->projectSettings.has_value()) { + return; + } - this->stopDebugServer(); - this->stopTargetController(); - this->stopSignalHandler(); + const auto projectSettingsPath = Paths::projectSettingsPath(); + auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath)); - this->saveProjectSettings(); - Thread::setThreadState(ThreadState::STOPPED); -} + Logger::debug("Saving project settings to " + projectSettingsPath); -void Application::loadProjectSettings() { - const auto projectSettingsPath = Paths::projectSettingsPath(); - auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath)); + QDir().mkpath(QString::fromStdString(Paths::projectSettingsDirPath())); - if (jsonSettingsFile.exists()) { try { - if (!jsonSettingsFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - throw Exception("Failed to open settings file."); + const auto jsonDocument = QJsonDocument(this->projectSettings->toJson()); + + if (!jsonSettingsFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { + throw Exception( + "failed to open/create settings file (" + projectSettingsPath + "). Check file permissions." + ); } - const auto jsonObject = QJsonDocument::fromJson(jsonSettingsFile.readAll()).object(); + jsonSettingsFile.write(jsonDocument.toJson()); jsonSettingsFile.close(); - this->projectSettings = ProjectSettings(jsonObject); - return; - - } catch (const std::exception& exception) { + } + catch (const Exception& exception) { Logger::error( - "Failed to load project settings from " + projectSettingsPath + " - " + exception.what() + "Failed to save project settings - " + exception.getMessage() ); } } - this->projectSettings = ProjectSettings(); -} + void Application::loadProjectConfiguration() { + auto jsonConfigFile = QFile(QString::fromStdString(Paths::projectConfigPath())); -void Application::saveProjectSettings() { - if (!this->projectSettings.has_value()) { - return; - } + if (!jsonConfigFile.exists()) { + throw InvalidConfig("Bloom configuration file (bloom.json) not found. Working directory: " + + Paths::projectDirPath()); + } - const auto projectSettingsPath = Paths::projectSettingsPath(); - auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath)); + if (!jsonConfigFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + throw InvalidConfig("Failed to load Bloom configuration file (bloom.json) Working directory: " + + Paths::projectDirPath()); + } - Logger::debug("Saving project settings to " + projectSettingsPath); + auto jsonObject = QJsonDocument::fromJson(jsonConfigFile.readAll()).object(); + jsonConfigFile.close(); - QDir().mkpath(QString::fromStdString(Paths::projectSettingsDirPath())); + this->projectConfig = ProjectConfig(jsonObject); - try { - const auto jsonDocument = QJsonDocument(this->projectSettings->toJson()); - - if (!jsonSettingsFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { - throw Exception( - "failed to open/create settings file (" + projectSettingsPath + "). Check file permissions." + // Validate the selected environment + if (!this->projectConfig->environments.contains(this->selectedEnvironmentName)) { + throw InvalidConfig( + "Environment (\"" + this->selectedEnvironmentName + "\") not found in configuration." ); } - jsonSettingsFile.write(jsonDocument.toJson()); - jsonSettingsFile.close(); + this->environmentConfig = this->projectConfig->environments.at(this->selectedEnvironmentName); - } catch (const Exception& exception) { - Logger::error( - "Failed to save project settings - " + exception.getMessage() - ); - } -} + if (this->environmentConfig->insightConfig.has_value()) { + this->insightConfig = this->environmentConfig->insightConfig.value(); -void Application::loadProjectConfiguration() { - auto jsonConfigFile = QFile(QString::fromStdString(Paths::projectConfigPath())); + } + else if (this->projectConfig->insightConfig.has_value()) { + this->insightConfig = this->projectConfig->insightConfig.value(); - if (!jsonConfigFile.exists()) { - throw InvalidConfig("Bloom configuration file (bloom.json) not found. Working directory: " - + Paths::projectDirPath()); + } + else { + throw InvalidConfig("Insight configuration missing."); + } + + if (this->environmentConfig->debugServerConfig.has_value()) { + this->debugServerConfig = this->environmentConfig->debugServerConfig.value(); + + } + else if (this->projectConfig->debugServerConfig.has_value()) { + this->debugServerConfig = this->projectConfig->debugServerConfig.value(); + + } + else { + throw InvalidConfig("Debug server configuration missing."); + } } - if (!jsonConfigFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - throw InvalidConfig("Failed to load Bloom configuration file (bloom.json) Working directory: " - + Paths::projectDirPath()); - } + int Application::presentHelpText() { + /* + * Silence all logging here, as we're just to display the help text and then exit the application. Any + * further logging will just be noise. + */ + Logger::silence(); - auto jsonObject = QJsonDocument::fromJson(jsonConfigFile.readAll()).object(); - jsonConfigFile.close(); - - this->projectConfig = ProjectConfig(jsonObject); - - // Validate the selected environment - if (!this->projectConfig->environments.contains(this->selectedEnvironmentName)) { - throw InvalidConfig( - "Environment (\"" + this->selectedEnvironmentName + "\") not found in configuration." - ); - } - - this->environmentConfig = this->projectConfig->environments.at(this->selectedEnvironmentName); - - if (this->environmentConfig->insightConfig.has_value()) { - this->insightConfig = this->environmentConfig->insightConfig.value(); - - } else if (this->projectConfig->insightConfig.has_value()) { - this->insightConfig = this->projectConfig->insightConfig.value(); - - } else { - throw InvalidConfig("Insight configuration missing."); - } - - if (this->environmentConfig->debugServerConfig.has_value()) { - this->debugServerConfig = this->environmentConfig->debugServerConfig.value(); - - } else if (this->projectConfig->debugServerConfig.has_value()) { - this->debugServerConfig = this->projectConfig->debugServerConfig.value(); - - } else { - throw InvalidConfig("Debug server configuration missing."); - } -} - -int Application::presentHelpText() { - /* - * Silence all logging here, as we're just to display the help text and then exit the application. Any - * further logging will just be noise. - */ - Logger::silence(); - - // The file help.txt is included in the binary image as a resource. See src/resource.qrc - auto helpFile = QFile(QString::fromStdString( + // The file help.txt is included in the binary image as a resource. See src/resource.qrc + auto helpFile = QFile(QString::fromStdString( Paths::compiledResourcesPath() - + "/resources/help.txt" - ) - ); - - if (!helpFile.open(QIODevice::ReadOnly)) { - // This should never happen - if it does, something has gone very wrong - throw Exception( - "Failed to open help file - please report this issue at " + Paths::homeDomainName() + "/report-issue" + + "/resources/help.txt" + ) ); + + if (!helpFile.open(QIODevice::ReadOnly)) { + // This should never happen - if it does, something has gone very wrong + throw Exception( + "Failed to open help file - please report this issue at " + Paths::homeDomainName() + + "/report-issue" + ); + } + + std::cout << "Bloom v" << Application::VERSION.toString() << "\n"; + std::cout << QTextStream(&helpFile).readAll().toUtf8().constData() << "\n"; + return EXIT_SUCCESS; } - std::cout << "Bloom v" << Application::VERSION.toString() << "\n"; - std::cout << QTextStream(&helpFile).readAll().toUtf8().constData() << "\n"; - return EXIT_SUCCESS; -} + int Application::presentVersionText() { + Logger::silence(); -int Application::presentVersionText() { - Logger::silence(); - - std::cout << "Bloom v" << Application::VERSION.toString() << "\n"; + std::cout << "Bloom v" << Application::VERSION.toString() << "\n"; #ifdef BLOOM_DEBUG_BUILD - std::cout << "DEBUG BUILD - Compilation timestamp: " << __DATE__ << " " << __TIME__ << "\n"; + std::cout << "DEBUG BUILD - Compilation timestamp: " << __DATE__ << " " << __TIME__ << "\n"; #endif - std::cout << Paths::homeDomainName() + "/\n"; - std::cout << "Nav Mohammed\n"; - return EXIT_SUCCESS; -} - -int Application::initProject() { - auto configFile = QFile(QString::fromStdString(std::filesystem::current_path().string() + "/bloom.json")); - - if (configFile.exists()) { - throw Exception("Bloom configuration file (bloom.json) already exists in working directory."); + std::cout << Paths::homeDomainName() + "/\n"; + std::cout << "Nav Mohammed\n"; + return EXIT_SUCCESS; } - /* - * The file bloom.template.json is just a template Bloom config file that is included in the binary image as - * a resource. See src/resource.qrc - * - * We simply copy the template file into the user's working directory. - */ - auto templateConfigFile = QFile(QString::fromStdString( - Paths::compiledResourcesPath() - + "/resources/bloom.template.json" - ) - ); - - if (!templateConfigFile.open(QIODevice::ReadOnly)) { - throw Exception( - "Failed to open template configuration file - please report this issue at " - + Paths::homeDomainName() + "/report-issue" + int Application::initProject() { + auto configFile = QFile( + QString::fromStdString(std::filesystem::current_path().string() + "/bloom.json") ); - } - if (!configFile.open(QIODevice::ReadWrite)) { - throw Exception("Failed to create Bloom configuration file (bloom.json)"); - } - - configFile.write(templateConfigFile.readAll()); - - configFile.close(); - templateConfigFile.close(); - - Logger::info("Bloom configuration file (bloom.json) created in working directory."); - return EXIT_SUCCESS; -} - -void Application::startSignalHandler() { - this->signalHandlerThread = std::thread(&SignalHandler::run, std::ref(this->signalHandler)); -} - -void Application::stopSignalHandler() { - if (this->signalHandler.getThreadState() != ThreadState::STOPPED - && this->signalHandler.getThreadState() != ThreadState::UNINITIALISED - ) { - this->signalHandler.triggerShutdown(); + if (configFile.exists()) { + throw Exception("Bloom configuration file (bloom.json) already exists in working directory."); + } /* - * Send meaningless signal to the SignalHandler thread to have it shutdown. The signal will pull it out of a - * blocking state and allow it to action the shutdown. See SignalHandler::run() for more. + * The file bloom.template.json is just a template Bloom config file that is included in the binary image as + * a resource. See src/resource.qrc + * + * We simply copy the template file into the user's working directory. */ - pthread_kill(this->signalHandlerThread.native_handle(), SIGUSR1); - } - - if (this->signalHandlerThread.joinable()) { - Logger::debug("Joining SignalHandler thread"); - this->signalHandlerThread.join(); - Logger::debug("SignalHandler thread joined"); - } -} - -void Application::startTargetController() { - this->targetController = std::make_unique( - this->eventManager, - this->projectConfig.value(), - this->environmentConfig.value() - ); - - this->targetControllerThread = std::thread( - &TargetController::run, - this->targetController.get() - ); - - auto tcStateChangeEvent = this->applicationEventListener->waitForEvent(); - - if (!tcStateChangeEvent.has_value() || tcStateChangeEvent->get()->getState() != ThreadState::READY) { - throw Exception("TargetController failed to startup."); - } -} - -void Application::stopTargetController() { - if (this->targetController == nullptr) { - return; - } - - auto targetControllerState = this->targetController->getThreadState(); - if (targetControllerState == ThreadState::STARTING || targetControllerState == ThreadState::READY) { - this->eventManager.triggerEvent(std::make_shared()); - this->applicationEventListener->waitForEvent( - std::chrono::milliseconds(10000) + auto templateConfigFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath()+ "/resources/bloom.template.json") ); + + if (!templateConfigFile.open(QIODevice::ReadOnly)) { + throw Exception( + "Failed to open template configuration file - please report this issue at " + + Paths::homeDomainName() + "/report-issue" + ); + } + + if (!configFile.open(QIODevice::ReadWrite)) { + throw Exception("Failed to create Bloom configuration file (bloom.json)"); + } + + configFile.write(templateConfigFile.readAll()); + + configFile.close(); + templateConfigFile.close(); + + Logger::info("Bloom configuration file (bloom.json) created in working directory."); + return EXIT_SUCCESS; } - if (this->targetControllerThread.joinable()) { - Logger::debug("Joining TargetController thread"); - this->targetControllerThread.join(); - Logger::debug("TargetController thread joined"); - } -} - -void Application::startDebugServer() { - auto supportedDebugServers = this->getSupportedDebugServers(); - if (!supportedDebugServers.contains(this->debugServerConfig->name)) { - throw Exceptions::InvalidConfig("DebugServer \"" + this->debugServerConfig->name + "\" not found."); + void Application::startSignalHandler() { + this->signalHandlerThread = std::thread(&SignalHandler::run, std::ref(this->signalHandler)); } - this->debugServer = supportedDebugServers.at(this->debugServerConfig->name)(); - Logger::info("Selected DebugServer: " + this->debugServer->getName()); + void Application::stopSignalHandler() { + if (this->signalHandler.getThreadState() != ThreadState::STOPPED + && this->signalHandler.getThreadState() != ThreadState::UNINITIALISED + ) { + this->signalHandler.triggerShutdown(); - this->debugServerThread = std::thread( - &DebugServers::DebugServer::run, - this->debugServer.get() - ); + /* + * Send meaningless signal to the SignalHandler thread to have it shutdown. The signal will pull it out of + * a blocking state and allow it to action the shutdown. See SignalHandler::run() for more. + */ + pthread_kill(this->signalHandlerThread.native_handle(), SIGUSR1); + } - auto dsStateChangeEvent = this->applicationEventListener->waitForEvent(); - - if (!dsStateChangeEvent.has_value() || dsStateChangeEvent->get()->getState() != ThreadState::READY) { - throw Exception("DebugServer failed to startup."); - } -} - -void Application::stopDebugServer() { - if (this->debugServer == nullptr) { - // DebugServer hasn't been resolved yet. - return; + if (this->signalHandlerThread.joinable()) { + Logger::debug("Joining SignalHandler thread"); + this->signalHandlerThread.join(); + Logger::debug("SignalHandler thread joined"); + } } - auto debugServerState = this->debugServer->getThreadState(); - if (debugServerState == ThreadState::STARTING || debugServerState == ThreadState::READY) { - this->eventManager.triggerEvent(std::make_shared()); - this->applicationEventListener->waitForEvent( - std::chrono::milliseconds(5000) + void Application::startTargetController() { + this->targetController = std::make_unique( + this->eventManager, + this->projectConfig.value(), + this->environmentConfig.value() ); + + this->targetControllerThread = std::thread( + &TargetController::run, + this->targetController.get() + ); + + auto tcStateChangeEvent = + this->applicationEventListener->waitForEvent(); + + if (!tcStateChangeEvent.has_value() || tcStateChangeEvent->get()->getState() != ThreadState::READY) { + throw Exception("TargetController failed to startup."); + } } - if (this->debugServerThread.joinable()) { - Logger::debug("Joining DebugServer thread"); - this->debugServerThread.join(); - Logger::debug("DebugServer thread joined"); - } -} + void Application::stopTargetController() { + if (this->targetController == nullptr) { + return; + } -void Application::onTargetControllerThreadStateChanged(const Events::TargetControllerThreadStateChanged& event) { - if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) { - // TargetController has unexpectedly shutdown - it must have encountered a fatal error. + auto targetControllerState = this->targetController->getThreadState(); + if (targetControllerState == ThreadState::STARTING || targetControllerState == ThreadState::READY) { + this->eventManager.triggerEvent(std::make_shared()); + this->applicationEventListener->waitForEvent( + std::chrono::milliseconds(10000) + ); + } + + if (this->targetControllerThread.joinable()) { + Logger::debug("Joining TargetController thread"); + this->targetControllerThread.join(); + Logger::debug("TargetController thread joined"); + } + } + + void Application::startDebugServer() { + auto supportedDebugServers = this->getSupportedDebugServers(); + if (!supportedDebugServers.contains(this->debugServerConfig->name)) { + throw Exceptions::InvalidConfig( + "DebugServer \"" + this->debugServerConfig->name + "\" not found." + ); + } + + this->debugServer = supportedDebugServers.at(this->debugServerConfig->name)(); + Logger::info("Selected DebugServer: " + this->debugServer->getName()); + + this->debugServerThread = std::thread( + &DebugServers::DebugServer::run, + this->debugServer.get() + ); + + auto dsStateChangeEvent = this->applicationEventListener->waitForEvent(); + + if (!dsStateChangeEvent.has_value() || dsStateChangeEvent->get()->getState() != ThreadState::READY) { + throw Exception("DebugServer failed to startup."); + } + } + + void Application::stopDebugServer() { + if (this->debugServer == nullptr) { + // DebugServer hasn't been resolved yet. + return; + } + + auto debugServerState = this->debugServer->getThreadState(); + if (debugServerState == ThreadState::STARTING || debugServerState == ThreadState::READY) { + this->eventManager.triggerEvent(std::make_shared()); + this->applicationEventListener->waitForEvent( + std::chrono::milliseconds(5000) + ); + } + + if (this->debugServerThread.joinable()) { + Logger::debug("Joining DebugServer thread"); + this->debugServerThread.join(); + Logger::debug("DebugServer thread joined"); + } + } + + void Application::onTargetControllerThreadStateChanged(const Events::TargetControllerThreadStateChanged& event) { + if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) { + // TargetController has unexpectedly shutdown - it must have encountered a fatal error. + this->shutdown(); + } + } + + void Application::onShutdownApplicationRequest(const Events::ShutdownApplication&) { + Logger::debug("ShutdownApplication event received."); this->shutdown(); } -} -void Application::onShutdownApplicationRequest(const Events::ShutdownApplication&) { - Logger::debug("ShutdownApplication event received."); - this->shutdown(); -} - -void Application::onDebugServerThreadStateChanged(const Events::DebugServerThreadStateChanged& event) { - if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) { - // DebugServer has unexpectedly shutdown - it must have encountered a fatal error. - this->shutdown(); + void Application::onDebugServerThreadStateChanged(const Events::DebugServerThreadStateChanged& event) { + if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) { + // DebugServer has unexpectedly shutdown - it must have encountered a fatal error. + this->shutdown(); + } } } diff --git a/src/DebugServers/DebugServer.cpp b/src/DebugServers/DebugServer.cpp index 1b6a1e07..ddb321de 100644 --- a/src/DebugServers/DebugServer.cpp +++ b/src/DebugServers/DebugServer.cpp @@ -5,58 +5,62 @@ #include "src/Exceptions/InvalidConfig.hpp" #include "src/Logger/Logger.hpp" -using namespace Bloom::DebugServers; -using namespace Bloom::Events; +namespace Bloom::DebugServers +{ + using namespace Bloom::Events; -void DebugServer::run() { - try { - this->startup(); + void DebugServer::run() { + try { + this->startup(); - Logger::info("DebugServer ready"); + Logger::info("DebugServer ready"); - while (this->getThreadState() == ThreadState::READY) { - this->serve(); - this->eventListener->dispatchCurrentEvents(); + while (this->getThreadState() == ThreadState::READY) { + this->serve(); + this->eventListener->dispatchCurrentEvents(); + } + } catch (const std::exception& exception) { + Logger::error("DebugServer fatal error: " + std::string(exception.what())); } - } catch (const std::exception& exception) { - Logger::error("DebugServer fatal error: " + std::string(exception.what())); + + this->shutdown(); } - this->shutdown(); -} + void DebugServer::startup() { + this->setName("DS"); + Logger::info("Starting DebugServer"); -void DebugServer::startup() { - this->setName("DS"); - Logger::info("Starting DebugServer"); + this->eventManager.registerListener(this->eventListener); - this->eventManager.registerListener(this->eventListener); + this->interruptEventNotifier = std::make_shared(); + this->eventListener->setInterruptEventNotifier(this->interruptEventNotifier); - this->interruptEventNotifier = std::make_shared(); - this->eventListener->setInterruptEventNotifier(this->interruptEventNotifier); + // Register event handlers + this->eventListener->registerCallbackForEventType( + std::bind(&DebugServer::onShutdownDebugServerEvent, this, std::placeholders::_1) + ); - // Register event handlers - this->eventListener->registerCallbackForEventType( - std::bind(&DebugServer::onShutdownDebugServerEvent, this, std::placeholders::_1) - ); + this->targetDescriptor = this->targetControllerConsole.getTargetDescriptor(); - this->targetDescriptor = this->targetControllerConsole.getTargetDescriptor(); - - this->init(); - this->setThreadStateAndEmitEvent(ThreadState::READY); -} - -void DebugServer::shutdown() { - if (this->getThreadState() == ThreadState::STOPPED || this->getThreadState() == ThreadState::SHUTDOWN_INITIATED) { - return; + this->init(); + this->setThreadStateAndEmitEvent(ThreadState::READY); } - this->setThreadState(ThreadState::SHUTDOWN_INITIATED); - Logger::info("Shutting down DebugServer"); - this->close(); - this->setThreadStateAndEmitEvent(ThreadState::STOPPED); - this->eventManager.deregisterListener(this->eventListener->getId()); -} + void DebugServer::shutdown() { + if (this->getThreadState() == ThreadState::STOPPED + || this->getThreadState() == ThreadState::SHUTDOWN_INITIATED + ) { + return; + } -void DebugServer::onShutdownDebugServerEvent(const Events::ShutdownDebugServer& event) { - this->shutdown(); + this->setThreadState(ThreadState::SHUTDOWN_INITIATED); + Logger::info("Shutting down DebugServer"); + this->close(); + this->setThreadStateAndEmitEvent(ThreadState::STOPPED); + this->eventManager.deregisterListener(this->eventListener->getId()); + } + + void DebugServer::onShutdownDebugServerEvent(const Events::ShutdownDebugServer& event) { + this->shutdown(); + } } diff --git a/src/DebugServers/GdbRsp/AvrGdbRsp/AvrGdbRsp.cpp b/src/DebugServers/GdbRsp/AvrGdbRsp/AvrGdbRsp.cpp index 45931eb4..164dff7e 100644 --- a/src/DebugServers/GdbRsp/AvrGdbRsp/AvrGdbRsp.cpp +++ b/src/DebugServers/GdbRsp/AvrGdbRsp/AvrGdbRsp.cpp @@ -2,139 +2,147 @@ #include "src/Exceptions/Exception.hpp" -using namespace Bloom::DebugServers::Gdb; -using namespace Bloom::Exceptions; +namespace Bloom::DebugServers::Gdb +{ + using namespace Bloom::Exceptions; -using Bloom::Targets::TargetRegisterDescriptor; -using Bloom::Targets::TargetRegisterType; + using Bloom::Targets::TargetRegisterDescriptor; + using Bloom::Targets::TargetRegisterType; -void AvrGdbRsp::init() { - this->loadRegisterMappings(); + void AvrGdbRsp::init() { + this->loadRegisterMappings(); - GdbRspDebugServer::init(); -} - -void AvrGdbRsp::loadRegisterMappings() { - auto& registerDescriptorsByType = this->targetDescriptor.registerDescriptorsByType; - if (!registerDescriptorsByType.contains(TargetRegisterType::STATUS_REGISTER)) { - throw Exception("Missing status register descriptor"); + GdbRspDebugServer::init(); } - if (!registerDescriptorsByType.contains(TargetRegisterType::STACK_POINTER)) { - throw Exception("Missing stack pointer register descriptor"); - } + void AvrGdbRsp::loadRegisterMappings() { + auto& registerDescriptorsByType = this->targetDescriptor.registerDescriptorsByType; + if (!registerDescriptorsByType.contains(TargetRegisterType::STATUS_REGISTER)) { + throw Exception("Missing status register descriptor"); + } - if (!registerDescriptorsByType.contains(TargetRegisterType::PROGRAM_COUNTER)) { - throw Exception("Missing program counter register descriptor"); - } + if (!registerDescriptorsByType.contains(TargetRegisterType::STACK_POINTER)) { + throw Exception("Missing stack pointer register descriptor"); + } - if (!registerDescriptorsByType.contains(TargetRegisterType::GENERAL_PURPOSE_REGISTER) - || registerDescriptorsByType.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).size() != 32 - ) { - throw Exception("Unexpected general purpose register count"); - } + if (!registerDescriptorsByType.contains(TargetRegisterType::PROGRAM_COUNTER)) { + throw Exception("Missing program counter register descriptor"); + } - /* - * Worth noting that gpRegisterDescriptors will always be sorted in the correct order, from register 0 to 31. - * - * Hmm, but the sorting is based on the start address (see TargetRegisterDescriptor::<() for more). So effectively, - * we're assuming that the registers will be laid out in the correct order, in memory. I think this assumption is - * fair. - */ - const auto& gpRegisterDescriptors = registerDescriptorsByType.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER); + if (!registerDescriptorsByType.contains(TargetRegisterType::GENERAL_PURPOSE_REGISTER) + || registerDescriptorsByType.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).size() != 32 + ) { + throw Exception("Unexpected general purpose register count"); + } - // General purpose CPU registers - GdbRegisterNumberType regNumber = 0; - for (const auto& descriptor : gpRegisterDescriptors) { - this->registerDescriptorsByGdbNumber.insert(std::pair( - regNumber, - RegisterDescriptor( + /* + * Worth noting that gpRegisterDescriptors will always be sorted in the correct order, from register 0 to 31. + * + * Hmm, but the sorting is based on the start address (see TargetRegisterDescriptor::<() for more). So effectively, + * we're assuming that the registers will be laid out in the correct order, in memory. I think this assumption is + * fair. + */ + const auto& gpRegisterDescriptors = registerDescriptorsByType.at( + TargetRegisterType::GENERAL_PURPOSE_REGISTER + ); + + // General purpose CPU registers + GdbRegisterNumberType regNumber = 0; + for (const auto& descriptor : gpRegisterDescriptors) { + this->registerDescriptorsByGdbNumber.insert(std::pair( regNumber, - 1, - "General Purpose Register " + std::to_string(regNumber) - ) - )); + RegisterDescriptor( + regNumber, + 1, + "General Purpose Register " + std::to_string(regNumber) + ) + )); + this->targetRegisterDescriptorsByGdbNumber.insert(std::pair( + regNumber, + descriptor + )); + + regNumber++; + } + + const auto statusDescriptor = RegisterDescriptor( + 32, + 1, + "Status Register" + ); + + this->registerDescriptorsByGdbNumber.insert(std::pair(statusDescriptor.number, statusDescriptor)); this->targetRegisterDescriptorsByGdbNumber.insert(std::pair( - regNumber, - descriptor + statusDescriptor.number, + *(registerDescriptorsByType.at(TargetRegisterType::STATUS_REGISTER).begin()) )); - regNumber++; + const auto stackPointerDescriptor = RegisterDescriptor( + 33, + 2, + "Stack Pointer Register" + ); + + this->registerDescriptorsByGdbNumber.insert( + std::pair(stackPointerDescriptor.number, stackPointerDescriptor) + ); + this->targetRegisterDescriptorsByGdbNumber.insert(std::pair( + stackPointerDescriptor.number, + *(registerDescriptorsByType.at(TargetRegisterType::STACK_POINTER).begin()) + )); + + const auto programCounterDescriptor = RegisterDescriptor( + 34, + 4, + "Program Counter" + ); + + this->registerDescriptorsByGdbNumber.insert(std::pair( + programCounterDescriptor.number, + programCounterDescriptor + )); + this->targetRegisterDescriptorsByGdbNumber.insert(std::pair( + programCounterDescriptor.number, + *(registerDescriptorsByType.at(TargetRegisterType::PROGRAM_COUNTER).begin()) + )); + + if (registerDescriptorsByType.at(TargetRegisterType::STATUS_REGISTER).size() > statusDescriptor.size) { + throw Exception("AVR8 status target register size exceeds the GDB register size."); + } + + if (registerDescriptorsByType.at(TargetRegisterType::STACK_POINTER).size() > stackPointerDescriptor.size) { + throw Exception("AVR8 stack pointer target register size exceeds the GDB register size."); + } + + if ( + registerDescriptorsByType.at(TargetRegisterType::PROGRAM_COUNTER).size() > programCounterDescriptor.size + ) { + throw Exception("AVR8 program counter size exceeds the GDB register size."); + } } - const auto statusDescriptor = RegisterDescriptor( - 32, - 1, - "Status Register" - ); - - this->registerDescriptorsByGdbNumber.insert(std::pair(statusDescriptor.number, statusDescriptor)); - this->targetRegisterDescriptorsByGdbNumber.insert(std::pair( - statusDescriptor.number, - *(registerDescriptorsByType.at(TargetRegisterType::STATUS_REGISTER).begin()) - )); - - const auto stackPointerDescriptor = RegisterDescriptor( - 33, - 2, - "Stack Pointer Register" - ); - - this->registerDescriptorsByGdbNumber.insert(std::pair(stackPointerDescriptor.number, stackPointerDescriptor)); - this->targetRegisterDescriptorsByGdbNumber.insert(std::pair( - stackPointerDescriptor.number, - *(registerDescriptorsByType.at(TargetRegisterType::STACK_POINTER).begin()) - )); - - const auto programCounterDescriptor = RegisterDescriptor( - 34, - 4, - "Program Counter" - ); - - this->registerDescriptorsByGdbNumber.insert(std::pair( - programCounterDescriptor.number, - programCounterDescriptor - )); - this->targetRegisterDescriptorsByGdbNumber.insert(std::pair( - programCounterDescriptor.number, - *(registerDescriptorsByType.at(TargetRegisterType::PROGRAM_COUNTER).begin()) - )); - - if (registerDescriptorsByType.at(TargetRegisterType::STATUS_REGISTER).size() > statusDescriptor.size) { - throw Exception("AVR8 status target register size exceeds the GDB register size."); + std::optional AvrGdbRsp::getRegisterNumberFromTargetRegisterDescriptor( + const Targets::TargetRegisterDescriptor& registerDescriptor + ) { + return this->targetRegisterDescriptorsByGdbNumber.valueAt(registerDescriptor); } - if (registerDescriptorsByType.at(TargetRegisterType::STACK_POINTER).size() > stackPointerDescriptor.size) { - throw Exception("AVR8 stack pointer target register size exceeds the GDB register size."); + const RegisterDescriptor& AvrGdbRsp::getRegisterDescriptorFromNumber(GdbRegisterNumberType number) { + if (this->registerDescriptorsByGdbNumber.contains(number)) { + return this->registerDescriptorsByGdbNumber.at(number); + } + + throw Exception("Unknown register from GDB - register number (" + std::to_string(number) + + ") not mapped to any GDB register descriptor."); } - if (registerDescriptorsByType.at(TargetRegisterType::PROGRAM_COUNTER).size() > programCounterDescriptor.size) { - throw Exception("AVR8 program counter size exceeds the GDB register size."); + const TargetRegisterDescriptor& AvrGdbRsp::getTargetRegisterDescriptorFromNumber(GdbRegisterNumberType number) { + if (this->targetRegisterDescriptorsByGdbNumber.contains(number)) { + return this->targetRegisterDescriptorsByGdbNumber.at(number); + } + + throw Exception("Unknown register from GDB - register number (" + std::to_string(number) + + ") not mapped to any target register descriptor."); } } - -std::optional AvrGdbRsp::getRegisterNumberFromTargetRegisterDescriptor( - const Targets::TargetRegisterDescriptor& registerDescriptor -) { - return this->targetRegisterDescriptorsByGdbNumber.valueAt(registerDescriptor); -} - -const RegisterDescriptor& AvrGdbRsp::getRegisterDescriptorFromNumber(GdbRegisterNumberType number) { - if (this->registerDescriptorsByGdbNumber.contains(number)) { - return this->registerDescriptorsByGdbNumber.at(number); - } - - throw Exception("Unknown register from GDB - register number (" + std::to_string(number) - + ") not mapped to any GDB register descriptor."); -} - -const TargetRegisterDescriptor& AvrGdbRsp::getTargetRegisterDescriptorFromNumber(GdbRegisterNumberType number) { - if (this->targetRegisterDescriptorsByGdbNumber.contains(number)) { - return this->targetRegisterDescriptorsByGdbNumber.at(number); - } - - throw Exception("Unknown register from GDB - register number (" + std::to_string(number) - + ") not mapped to any target register descriptor."); -} diff --git a/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp b/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp index 6a285f6d..64e6595c 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp @@ -2,8 +2,9 @@ #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; - -void CommandPacket::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + void CommandPacket::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp b/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp index 973b358c..cd846eef 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp @@ -4,126 +4,138 @@ #include #include -using namespace Bloom::DebugServers::Gdb; -using namespace Bloom::DebugServers::Gdb::CommandPackets; +namespace Bloom::DebugServers::Gdb +{ + using CommandPackets::CommandPacket; -std::vector> CommandPacketFactory::extractRawPackets(std::vector buffer) { - std::vector> output; + std::vector> CommandPacketFactory::extractRawPackets( + std::vector buffer + ) { + std::vector> output; - std::size_t bufferIndex; - std::size_t bufferSize = buffer.size(); - unsigned char byte; - for (bufferIndex = 0; bufferIndex < bufferSize; bufferIndex++) { - byte = buffer[bufferIndex]; + std::size_t bufferIndex; + std::size_t bufferSize = buffer.size(); + unsigned char byte; + for (bufferIndex = 0; bufferIndex < bufferSize; bufferIndex++) { + byte = buffer[bufferIndex]; - if (byte == 0x03) { - /* - * This is an interrupt packet - it doesn't carry any of the usual packet frame bytes, so we'll just - * add them here, in order to keep things consistent. - * - * Because we're effectively faking the packet frame, we can use any value for the checksum. - */ - output.push_back({'$', byte, '#', 'F', 'F'}); + if (byte == 0x03) { + /* + * This is an interrupt packet - it doesn't carry any of the usual packet frame bytes, so we'll just + * add them here, in order to keep things consistent. + * + * Because we're effectively faking the packet frame, we can use any value for the checksum. + */ + output.push_back({'$', byte, '#', 'F', 'F'}); - } else if (byte == '$') { - // Beginning of packet - std::vector rawPacket; - rawPacket.push_back('$'); + } else if (byte == '$') { + // Beginning of packet + std::vector rawPacket; + rawPacket.push_back('$'); - auto packetIndex = bufferIndex; - bool validPacket = false; - bool isByteEscaped = false; + auto packetIndex = bufferIndex; + bool validPacket = false; + bool isByteEscaped = false; - for (packetIndex++; packetIndex < bufferSize; packetIndex++) { - byte = buffer[packetIndex]; + for (packetIndex++; packetIndex < bufferSize; packetIndex++) { + byte = buffer[packetIndex]; - if (byte == '}' && !isByteEscaped) { - isByteEscaped = true; - continue; - } + if (byte == '}' && !isByteEscaped) { + isByteEscaped = true; + continue; + } - if (byte == '$' && !isByteEscaped) { - // Unexpected end of packet - validPacket = false; - break; - } - - if (byte == '#' && !isByteEscaped) { - // End of packet data - if ((bufferSize - 1) < (packetIndex + 2)) { - // There should be at least two more bytes in the buffer, for the checksum. + if (byte == '$' && !isByteEscaped) { + // Unexpected end of packet + validPacket = false; break; } + if (byte == '#' && !isByteEscaped) { + // End of packet data + if ((bufferSize - 1) < (packetIndex + 2)) { + // There should be at least two more bytes in the buffer, for the checksum. + break; + } + + rawPacket.push_back(byte); + + // Add the checksum bytes and break the loop + rawPacket.push_back(buffer[++packetIndex]); + rawPacket.push_back(buffer[++packetIndex]); + validPacket = true; + break; + } + + if (isByteEscaped) { + // Escaped bytes are XOR'd with a 0x20 mask. + byte ^= 0x20; + isByteEscaped = false; + } + rawPacket.push_back(byte); - - // Add the checksum bytes and break the loop - rawPacket.push_back(buffer[++packetIndex]); - rawPacket.push_back(buffer[++packetIndex]); - validPacket = true; - break; } - if (isByteEscaped) { - // Escaped bytes are XOR'd with a 0x20 mask. - byte ^= 0x20; - isByteEscaped = false; + if (validPacket) { + output.push_back(rawPacket); + bufferIndex = packetIndex; } - - rawPacket.push_back(byte); - } - - if (validPacket) { - output.push_back(rawPacket); - bufferIndex = packetIndex; } } + + return output; } - return output; -} - -std::unique_ptr CommandPacketFactory::create(std::vector rawPacket) { - if (rawPacket.size() == 5 && rawPacket[1] == 0x03) { - // This is an interrupt request - create a fake packet for it - return std::make_unique(rawPacket); - } - - auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end()); - - if (rawPacketString.size() >= 2) { - /* - * First byte of the raw packet will be 0x24 ('$'), so find() should return 1, not 0, when - * looking for a command identifier string. - */ - if (rawPacketString.find("qSupported") == 1) { - return std::make_unique(rawPacket); - - } else if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') { - return std::make_unique(rawPacket); - - } else if (rawPacketString[1] == 'P') { - return std::make_unique(rawPacket); - - } else if (rawPacketString[1] == 'c') { - return std::make_unique(rawPacket); - - } else if (rawPacketString[1] == 's') { - return std::make_unique(rawPacket); - - } else if (rawPacketString[1] == 'm') { - return std::make_unique(rawPacket); - - } else if (rawPacketString[1] == 'M') { - return std::make_unique(rawPacket); - - } else if (rawPacketString[1] == 'Z') { - return std::make_unique(rawPacket); - - } else if (rawPacketString[1] == 'z') { - return std::make_unique(rawPacket); + std::unique_ptr CommandPacketFactory::create(std::vector rawPacket) { + if (rawPacket.size() == 5 && rawPacket[1] == 0x03) { + // This is an interrupt request - create a fake packet for it + return std::make_unique(rawPacket); } - } - return std::make_unique(rawPacket); + auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end()); + + if (rawPacketString.size() >= 2) { + /* + * First byte of the raw packet will be 0x24 ('$'), so find() should return 1, not 0, when + * looking for a command identifier string. + */ + if (rawPacketString.find("qSupported") == 1) { + return std::make_unique(rawPacket); + } + + if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') { + return std::make_unique(rawPacket); + } + + if (rawPacketString[1] == 'P') { + return std::make_unique(rawPacket); + } + + if (rawPacketString[1] == 'c') { + return std::make_unique(rawPacket); + } + + if (rawPacketString[1] == 's') { + return std::make_unique(rawPacket); + } + + if (rawPacketString[1] == 'm') { + return std::make_unique(rawPacket); + } + + if (rawPacketString[1] == 'M') { + return std::make_unique(rawPacket); + } + + if (rawPacketString[1] == 'Z') { + return std::make_unique(rawPacket); + } + + if (rawPacketString[1] == 'z') { + return std::make_unique(rawPacket); + } + } + + return std::make_unique(rawPacket); + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp b/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp index ef9d882b..6058e4f7 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp @@ -4,16 +4,17 @@ #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); + } -void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); -} - -void ContinueExecution::init() { - if (this->data.size() > 1) { - this->fromProgramCounter = static_cast( - std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16) - ); + void ContinueExecution::init() { + if (this->data.size() > 1) { + this->fromProgramCounter = static_cast( + std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16) + ); + } } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp b/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp index dd6a1a1a..5bba19f3 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp @@ -2,8 +2,9 @@ #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; - -void InterruptExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + void InterruptExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/ReadMemory.cpp b/src/DebugServers/GdbRsp/CommandPackets/ReadMemory.cpp index 77f4802d..ce68b108 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/ReadMemory.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/ReadMemory.cpp @@ -2,43 +2,47 @@ #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; -using namespace Bloom::Exceptions; +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + using namespace Bloom::Exceptions; -void ReadMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); -} - -void ReadMemory::init() { - if (this->data.size() < 4) { - throw Exception("Invalid packet length"); + void ReadMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); } - auto packetString = QString::fromLocal8Bit( - reinterpret_cast(this->data.data() + 1), - static_cast(this->data.size() - 1) - ); + void ReadMemory::init() { + if (this->data.size() < 4) { + throw Exception("Invalid packet length"); + } - /* - * The read memory ('m') packet consists of two segments, an address and a number of bytes to read. - * These are separated by a comma character. - */ - auto packetSegments = packetString.split(","); + auto packetString = QString::fromLocal8Bit( + reinterpret_cast(this->data.data() + 1), + static_cast(this->data.size() - 1) + ); - if (packetSegments.size() != 2) { - throw Exception("Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())); - } + /* + * The read memory ('m') packet consists of two segments, an address and a number of bytes to read. + * These are separated by a comma character. + */ + auto packetSegments = packetString.split(","); - bool conversionStatus = false; - this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16); + if (packetSegments.size() != 2) { + throw Exception( + "Unexpected number of segments in packet data: " + std::to_string(packetSegments.size()) + ); + } - if (!conversionStatus) { - throw Exception("Failed to parse start address from read memory packet data"); - } + bool conversionStatus = false; + this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16); - this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16); + if (!conversionStatus) { + throw Exception("Failed to parse start address from read memory packet data"); + } - if (!conversionStatus) { - throw Exception("Failed to parse read length from read memory packet data"); + this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16); + + if (!conversionStatus) { + throw Exception("Failed to parse read length from read memory packet data"); + } } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp b/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp index 9326a7c7..b1068a68 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp @@ -2,15 +2,18 @@ #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + void ReadRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); + } -void ReadRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); -} - -void ReadRegisters::init() { - if (this->data.size() >= 2 && this->data.front() == 'p') { - // This command packet is requesting a specific register - this->registerNumber = static_cast(std::stoi(std::string(this->data.begin() + 1, this->data.end()))); + void ReadRegisters::init() { + if (this->data.size() >= 2 && this->data.front() == 'p') { + // This command packet is requesting a specific register + this->registerNumber = static_cast( + std::stoi(std::string(this->data.begin() + 1, this->data.end())) + ); + } } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp b/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp index 22a21699..ddba1777 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp @@ -4,36 +4,38 @@ #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; -using namespace Bloom::Exceptions; +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + using namespace Bloom::Exceptions; -void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); -} - -void RemoveBreakpoint::init() { - if (data.size() < 6) { - throw Exception("Unexpected RemoveBreakpoint packet size"); + void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); } - // z0 = SW breakpoint, z1 = HW breakpoint - this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ? - BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN; + void RemoveBreakpoint::init() { + if (data.size() < 6) { + throw Exception("Unexpected RemoveBreakpoint packet size"); + } - auto packetData = QString::fromLocal8Bit( - reinterpret_cast(this->data.data() + 2), - static_cast(this->data.size() - 2) - ); + // z0 = SW breakpoint, z1 = HW breakpoint + this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ? + BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN; - auto packetSegments = packetData.split(","); - if (packetSegments.size() < 3) { - throw Exception("Unexpected number of packet segments in RemoveBreakpoint packet"); - } + auto packetData = QString::fromLocal8Bit( + reinterpret_cast(this->data.data() + 2), + static_cast(this->data.size() - 2) + ); - bool conversionStatus = true; - this->address = packetSegments.at(1).toUInt(&conversionStatus, 16); + auto packetSegments = packetData.split(","); + if (packetSegments.size() < 3) { + throw Exception("Unexpected number of packet segments in RemoveBreakpoint packet"); + } - if (!conversionStatus) { - throw Exception("Failed to convert address hex value from RemoveBreakpoint packet."); + bool conversionStatus = true; + this->address = packetSegments.at(1).toUInt(&conversionStatus, 16); + + if (!conversionStatus) { + throw Exception("Failed to convert address hex value from RemoveBreakpoint packet."); + } } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp b/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp index 0c1e32c1..7c592e3c 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp @@ -1,40 +1,41 @@ #include "SetBreakpoint.hpp" #include -#include #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; -using namespace Bloom::Exceptions; +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + using namespace Bloom::Exceptions; -void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); -} - -void SetBreakpoint::init() { - if (data.size() < 6) { - throw Exception("Unexpected SetBreakpoint packet size"); + void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); } - // Z0 = SW breakpoint, Z1 = HW breakpoint - this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ? - BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN; + void SetBreakpoint::init() { + if (data.size() < 6) { + throw Exception("Unexpected SetBreakpoint packet size"); + } - auto packetData = QString::fromLocal8Bit( - reinterpret_cast(this->data.data() + 2), - static_cast(this->data.size() - 2) - ); + // Z0 = SW breakpoint, Z1 = HW breakpoint + this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ? + BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN; - auto packetSegments = packetData.split(","); - if (packetSegments.size() < 3) { - throw Exception("Unexpected number of packet segments in SetBreakpoint packet"); - } + auto packetData = QString::fromLocal8Bit( + reinterpret_cast(this->data.data() + 2), + static_cast(this->data.size() - 2) + ); - bool conversionStatus = true; - this->address = packetSegments.at(1).toUInt(&conversionStatus, 16); + auto packetSegments = packetData.split(","); + if (packetSegments.size() < 3) { + throw Exception("Unexpected number of packet segments in SetBreakpoint packet"); + } - if (!conversionStatus) { - throw Exception("Failed to convert address hex value from SetBreakpoint packet."); + bool conversionStatus = true; + this->address = packetSegments.at(1).toUInt(&conversionStatus, 16); + + if (!conversionStatus) { + throw Exception("Failed to convert address hex value from SetBreakpoint packet."); + } } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp b/src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp index 2935d15a..584d8400 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp @@ -1,18 +1,20 @@ +#include "StepExecution.hpp" + #include #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" -#include "StepExecution.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); + } -void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); -} - -void StepExecution::init() { - if (this->data.size() > 1) { - this->fromProgramCounter = static_cast( - std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16) - ); + void StepExecution::init() { + if (this->data.size() > 1) { + this->fromProgramCounter = static_cast( + std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16) + ); + } } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/StepExecution.hpp b/src/DebugServers/GdbRsp/CommandPackets/StepExecution.hpp index 96166b8b..21d88c1d 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/StepExecution.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/StepExecution.hpp @@ -16,7 +16,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets /** * The address from which to begin the step. */ - std::optional fromProgramCounter; + std::optional fromProgramCounter; explicit StepExecution(const std::vector& rawPacket): CommandPacket(rawPacket) { init(); diff --git a/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp b/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp index 760b4087..f88a3b25 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp @@ -4,38 +4,39 @@ #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); + } -void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); -} + void SupportedFeaturesQuery::init() { + /* + * For qSupported packets, supported and unsupported GDB features are reported in the packet + * data, where each GDB feature is separated by a semicolon. + */ -void SupportedFeaturesQuery::init() { - /* - * For qSupported packets, supported and unsupported GDB features are reported in the packet - * data, where each GDB feature is separated by a semicolon. - */ + // The "qSupported:" prefix occupies 11 bytes + if (data.size() > 11) { + auto packetData = QString::fromLocal8Bit( + reinterpret_cast(this->data.data() + 11), + static_cast(this->data.size() - 11) + ); - // The "qSupported:" prefix occupies 11 bytes - if (data.size() > 11) { - auto packetData = QString::fromLocal8Bit( - reinterpret_cast(this->data.data() + 11), - static_cast(this->data.size() - 11) - ); + auto featureList = packetData.split(";"); + auto gdbFeatureMapping = getGdbFeatureToNameMapping(); - auto featureList = packetData.split(";"); - auto gdbFeatureMapping = getGdbFeatureToNameMapping(); + for (int i = 0; i < featureList.size(); i++) { + auto featureString = featureList.at(i); - for (int i = 0; i < featureList.size(); i++) { - auto featureString = featureList.at(i); + // We only care about supported features. Supported features will precede a '+' character. + if (featureString[featureString.size() - 1] == '+') { + featureString.remove('+'); - // We only care about supported features. Supported features will precede a '+' character. - if (featureString[featureString.size() - 1] == '+') { - featureString.remove('+'); - - auto feature = gdbFeatureMapping.valueAt(featureString.toStdString()); - if (feature.has_value()) { - this->supportedFeatures.insert(static_cast(feature.value())); + auto feature = gdbFeatureMapping.valueAt(featureString.toStdString()); + if (feature.has_value()) { + this->supportedFeatures.insert(static_cast(feature.value())); + } } } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/WriteMemory.cpp b/src/DebugServers/GdbRsp/CommandPackets/WriteMemory.cpp index 8e3214c5..06439a57 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/WriteMemory.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/WriteMemory.cpp @@ -2,52 +2,59 @@ #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; -using namespace Bloom::Exceptions; +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + using namespace Bloom::Exceptions; -void WriteMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); -} - -void WriteMemory::init() { - if (this->data.size() < 4) { - throw Exception("Invalid packet length"); + void WriteMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); } - auto packetString = QString::fromLocal8Bit( - reinterpret_cast(this->data.data() + 1), - static_cast(this->data.size() - 1) - ); + void WriteMemory::init() { + if (this->data.size() < 4) { + throw Exception("Invalid packet length"); + } - /* - * The write memory ('M') packet consists of three segments, an address, a length and a buffer. - * The address and length are separated by a comma character, and the buffer proceeds a colon character. - */ - auto packetSegments = packetString.split(","); - if (packetSegments.size() != 2) { - throw Exception("Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())); - } + auto packetString = QString::fromLocal8Bit( + reinterpret_cast(this->data.data() + 1), + static_cast(this->data.size() - 1) + ); - bool conversionStatus = false; - this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16); + /* + * The write memory ('M') packet consists of three segments, an address, a length and a buffer. + * The address and length are separated by a comma character, and the buffer proceeds a colon character. + */ + auto packetSegments = packetString.split(","); + if (packetSegments.size() != 2) { + throw Exception( + "Unexpected number of segments in packet data: " + std::to_string(packetSegments.size()) + ); + } - if (!conversionStatus) { - throw Exception("Failed to parse start address from write memory packet data"); - } + bool conversionStatus = false; + this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16); - auto lengthAndBufferSegments = packetSegments.at(1).split(":"); - if (lengthAndBufferSegments.size() != 2) { - throw Exception("Unexpected number of segments in packet data: " + std::to_string(lengthAndBufferSegments.size())); - } + if (!conversionStatus) { + throw Exception("Failed to parse start address from write memory packet data"); + } - auto bufferSize = lengthAndBufferSegments.at(0).toUInt(&conversionStatus, 16); - if (!conversionStatus) { - throw Exception("Failed to parse write length from write memory packet data"); - } + auto lengthAndBufferSegments = packetSegments.at(1).split(":"); + if (lengthAndBufferSegments.size() != 2) { + throw Exception( + "Unexpected number of segments in packet data: " + + std::to_string(lengthAndBufferSegments.size()) + ); + } - this->buffer = Packet::hexToData(lengthAndBufferSegments.at(1).toStdString()); + auto bufferSize = lengthAndBufferSegments.at(0).toUInt(&conversionStatus, 16); + if (!conversionStatus) { + throw Exception("Failed to parse write length from write memory packet data"); + } - if (this->buffer.size() != bufferSize) { - throw Exception("Buffer size does not match length value given in write memory packet"); + this->buffer = Packet::hexToData(lengthAndBufferSegments.at(1).toStdString()); + + if (this->buffer.size() != bufferSize) { + throw Exception("Buffer size does not match length value given in write memory packet"); + } } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp b/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp index dcd75d0b..fb1c7a1c 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp @@ -3,27 +3,29 @@ #include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/Exceptions/Exception.hpp" -using namespace Bloom::DebugServers::Gdb::CommandPackets; -using namespace Bloom::Exceptions; +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + using namespace Bloom::Exceptions; -void WriteRegister::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); -} - -void WriteRegister::init() { - // The P packet updates a single register - auto packet = std::string(this->data.begin(), this->data.end()); - - if (packet.size() < 4) { - throw Exception("Invalid P command packet - insufficient data in packet."); + void WriteRegister::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { + gdbRspDebugServer.handleGdbPacket(*this); } - if (packet.find('=') == std::string::npos) { - throw Exception("Invalid P command packet - unexpected format"); - } + void WriteRegister::init() { + // The P packet updates a single register + auto packet = std::string(this->data.begin(), this->data.end()); - auto packetSegments = QString::fromStdString(packet).split("="); - this->registerNumber = static_cast(packetSegments.front().mid(1).toUInt(nullptr, 16)); - this->registerValue = Packet::hexToData(packetSegments.back().toStdString()); - std::reverse(this->registerValue.begin(), this->registerValue.end()); + if (packet.size() < 4) { + throw Exception("Invalid P command packet - insufficient data in packet."); + } + + if (packet.find('=') == std::string::npos) { + throw Exception("Invalid P command packet - unexpected format"); + } + + auto packetSegments = QString::fromStdString(packet).split("="); + this->registerNumber = static_cast(packetSegments.front().mid(1).toUInt(nullptr, 16)); + this->registerValue = Packet::hexToData(packetSegments.back().toStdString()); + std::reverse(this->registerValue.begin(), this->registerValue.end()); + } } diff --git a/src/DebugServers/GdbRsp/Connection.cpp b/src/DebugServers/GdbRsp/Connection.cpp index c58b7e5b..8ee1b4d5 100644 --- a/src/DebugServers/GdbRsp/Connection.cpp +++ b/src/DebugServers/GdbRsp/Connection.cpp @@ -14,199 +14,206 @@ #include "src/Logger/Logger.hpp" -using namespace Bloom::DebugServers::Gdb; -using namespace Bloom::DebugServers::Gdb::CommandPackets; -using namespace Bloom::DebugServers::Gdb::ResponsePackets; -using namespace Bloom::DebugServers::Gdb::Exceptions; -using namespace Bloom::Exceptions; +namespace Bloom::DebugServers::Gdb +{ + using namespace CommandPackets; + using namespace ResponsePackets; + using namespace Exceptions; + using namespace Bloom::Exceptions; -void Connection::accept(int serverSocketFileDescriptor) { - int socketAddressLength = sizeof(this->socketAddress); + void Connection::accept(int serverSocketFileDescriptor) { + int socketAddressLength = sizeof(this->socketAddress); - this->socketFileDescriptor = ::accept( - serverSocketFileDescriptor, - (sockaddr*)& (this->socketAddress), - (socklen_t*)& socketAddressLength - ); + this->socketFileDescriptor = ::accept( + serverSocketFileDescriptor, + (sockaddr*) &(this->socketAddress), + (socklen_t*) &socketAddressLength + ); - if (this->socketFileDescriptor == -1) { - throw Exception("Failed to accept GDB Remote Serial Protocol connection"); + if (this->socketFileDescriptor == -1) { + throw Exception("Failed to accept GDB Remote Serial Protocol connection"); + } + + ::fcntl( + this->socketFileDescriptor, + F_SETFL, + fcntl(this->socketFileDescriptor, F_GETFL, 0) | O_NONBLOCK + ); + + // Create event FD + this->eventFileDescriptor = ::epoll_create(2); + struct epoll_event event = {}; + event.events = EPOLLIN; + event.data.fd = this->socketFileDescriptor; + + if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->socketFileDescriptor, &event) != 0) { + throw Exception( + "Failed to create event FD for GDB client connection - could not add client connection " + "socket FD to epoll FD" + ); + } + + this->enableReadInterrupts(); } - ::fcntl( - this->socketFileDescriptor, - F_SETFL, - fcntl(this->socketFileDescriptor, F_GETFL, 0) | O_NONBLOCK - ); - - // Create event FD - this->eventFileDescriptor = ::epoll_create(2); - struct epoll_event event = {}; - event.events = EPOLLIN; - event.data.fd = this->socketFileDescriptor; - - if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->socketFileDescriptor, &event) != 0) { - throw Exception("Failed to create event FD for GDB client connection - could not add client connection " - "socket FD to epoll FD"); - } - - this->enableReadInterrupts(); -} - -void Connection::close() noexcept { - if (this->socketFileDescriptor > 0) { - ::close(this->socketFileDescriptor); - this->socketFileDescriptor = 0; - } -} - -std::vector> Connection::readPackets() { - auto buffer = this->read(); - Logger::debug("GDB client data received (" + std::to_string(buffer.size()) + " bytes): " + std::string(buffer.begin(), buffer.end())); - - auto rawPackets = CommandPacketFactory::extractRawPackets(buffer); - std::vector> output; - - for (const auto& rawPacket : rawPackets) { - try { - output.push_back(CommandPacketFactory::create(rawPacket)); - this->write({'+'}); - - } catch (const ClientDisconnected& exception) { - throw exception; - - } catch (const Exception& exception) { - Logger::error("Failed to parse GDB packet - " + exception.getMessage()); - this->write({'-'}); + void Connection::close() noexcept { + if (this->socketFileDescriptor > 0) { + ::close(this->socketFileDescriptor); + this->socketFileDescriptor = 0; } } - return output; -} + std::vector> Connection::readPackets() { + auto buffer = this->read(); + Logger::debug("GDB client data received (" + std::to_string(buffer.size()) + " bytes): " + + std::string(buffer.begin(), buffer.end())); -void Connection::writePacket(const ResponsePacket& packet) { - // Write the packet repeatedly until the GDB client acknowledges it. - int attempts = 0; - auto rawPacket = packet.toRawPacket(); + auto rawPackets = CommandPacketFactory::extractRawPackets(buffer); + std::vector> output; - do { - if (attempts > 10) { - throw ClientCommunicationError("Failed to write GDB response packet - client failed to " - "acknowledge receipt - retry limit reached"); + for (const auto& rawPacket : rawPackets) { + try { + output.push_back(CommandPacketFactory::create(rawPacket)); + this->write({'+'}); + + } catch (const ClientDisconnected& exception) { + throw exception; + + } catch (const Exception& exception) { + Logger::error("Failed to parse GDB packet - " + exception.getMessage()); + this->write({'-'}); + } } - this->write(rawPacket); - attempts++; - - } while(this->readSingleByte(false).value_or(0) != '+'); -} - -std::vector Connection::read(size_t bytes, bool interruptible, std::optional msTimeout) { - auto output = std::vector(); - constexpr size_t bufferSize = 1024; - std::array buffer = {}; - ssize_t bytesRead; - - if (interruptible) { - if (this->readInterruptEnabled != interruptible) { - this->enableReadInterrupts(); - } else { - // Clear any previous interrupts that are still hanging around - this->interruptEventNotifier->clear(); - } + return output; } - if (this->readInterruptEnabled != interruptible && !interruptible) { - this->disableReadInterrupts(); + void Connection::writePacket(const ResponsePacket& packet) { + // Write the packet repeatedly until the GDB client acknowledges it. + int attempts = 0; + auto rawPacket = packet.toRawPacket(); + + do { + if (attempts > 10) { + throw ClientCommunicationError("Failed to write GDB response packet - client failed to " + "acknowledge receipt - retry limit reached"); + } + + this->write(rawPacket); + attempts++; + } while (this->readSingleByte(false).value_or(0) != '+'); } - std::array events = {}; + std::vector Connection::read(size_t bytes, bool interruptible, std::optional msTimeout) { + auto output = std::vector(); + constexpr size_t bufferSize = 1024; + std::array buffer = {}; + ssize_t bytesRead; - int eventCount = ::epoll_wait( - this->eventFileDescriptor, - events.data(), - 1, - msTimeout.value_or(-1) - ); + if (interruptible) { + if (this->readInterruptEnabled != interruptible) { + this->enableReadInterrupts(); - if (eventCount > 0) { - for (size_t i = 0; i < eventCount; i++) { - auto fileDescriptor = events[i].data.fd; - - if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) { - // Interrupted + } else { + // Clear any previous interrupts that are still hanging around this->interruptEventNotifier->clear(); - throw DebugServerInterrupted(); } } - size_t bytesToRead = (bytes > bufferSize || bytes == 0) ? bufferSize : bytes; - while (bytesToRead > 0 && (bytesRead = ::read(this->socketFileDescriptor, buffer.data(), bytesToRead)) > 0) { - output.insert(output.end(), buffer.begin(), buffer.begin() + bytesRead); + if (this->readInterruptEnabled != interruptible && !interruptible) { + this->disableReadInterrupts(); + } - if (bytesRead < bytesToRead) { - // No more data available - break; + std::array events = {}; + + int eventCount = ::epoll_wait( + this->eventFileDescriptor, + events.data(), + 1, + msTimeout.value_or(-1) + ); + + if (eventCount > 0) { + for (size_t i = 0; i < eventCount; i++) { + auto fileDescriptor = events[i].data.fd; + + if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) { + // Interrupted + this->interruptEventNotifier->clear(); + throw DebugServerInterrupted(); + } } - bytesToRead = ((bytes - output.size()) > bufferSize || bytes == 0) ? bufferSize : (bytes - output.size()); + size_t bytesToRead = (bytes > bufferSize || bytes == 0) ? bufferSize : bytes; + while (bytesToRead > 0 + && (bytesRead = ::read(this->socketFileDescriptor, buffer.data(), bytesToRead)) > 0) { + output.insert(output.end(), buffer.begin(), buffer.begin() + bytesRead); + + if (bytesRead < bytesToRead) { + // No more data available + break; + } + + bytesToRead = + ((bytes - output.size()) > bufferSize || bytes == 0) ? bufferSize : (bytes - output.size()); + } + + if (output.empty()) { + // EOF means the client has disconnected + throw ClientDisconnected(); + } } - if (output.empty()) { - // EOF means the client has disconnected - throw ClientDisconnected(); + return output; + } + + std::optional Connection::readSingleByte(bool interruptible) { + auto bytes = this->read(1, interruptible, 300); + + if (!bytes.empty()) { + return bytes.front(); } + + return std::nullopt; } - return output; -} + void Connection::write(const std::vector& buffer) { + Logger::debug("Writing packet: " + std::string(buffer.begin(), buffer.end())); + if (::write(this->socketFileDescriptor, buffer.data(), buffer.size()) == -1) { + if (errno == EPIPE || errno == ECONNRESET) { + // Connection was closed + throw ClientDisconnected(); + } -std::optional Connection::readSingleByte(bool interruptible) { - auto bytes = this->read(1, interruptible, 300); - - if (!bytes.empty()) { - return bytes.front(); - } - - return std::nullopt; -} - -void Connection::write(const std::vector& buffer) { - Logger::debug("Writing packet: " + std::string(buffer.begin(), buffer.end())); - if (::write(this->socketFileDescriptor, buffer.data(), buffer.size()) == -1) { - if (errno == EPIPE || errno == ECONNRESET) { - // Connection was closed - throw ClientDisconnected(); - - } else { throw ClientCommunicationError("Failed to write " + std::to_string(buffer.size()) - + " bytes to GDP client socket - error no: " + std::to_string(errno)); + + " bytes to GDP client socket - error no: " + + std::to_string(errno)); } } -} -void Connection::disableReadInterrupts() { - if (::epoll_ctl( - this->eventFileDescriptor, - EPOLL_CTL_DEL, - this->interruptEventNotifier->getFileDescriptor(), - NULL) != 0 - ) { - throw Exception("Failed to disable GDB client connection read interrupts - epoll_ctl failed"); + void Connection::disableReadInterrupts() { + if (::epoll_ctl( + this->eventFileDescriptor, + EPOLL_CTL_DEL, + this->interruptEventNotifier->getFileDescriptor(), + NULL) != 0 + ) { + throw Exception("Failed to disable GDB client connection read interrupts - epoll_ctl failed"); + } + + this->readInterruptEnabled = false; } - this->readInterruptEnabled = false; -} + void Connection::enableReadInterrupts() { + auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor(); + struct epoll_event event = {}; + event.events = EPOLLIN; + event.data.fd = interruptFileDescriptor; -void Connection::enableReadInterrupts() { - auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor(); - struct epoll_event event = {}; - event.events = EPOLLIN; - event.data.fd = interruptFileDescriptor; + if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) { + throw Exception("Failed to enable GDB client connection read interrupts - epoll_ctl failed"); + } - if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) { - throw Exception("Failed to enable GDB client connection read interrupts - epoll_ctl failed"); + this->readInterruptEnabled = true; } - - this->readInterruptEnabled = true; } diff --git a/src/DebugServers/GdbRsp/GdbRspDebugServer.cpp b/src/DebugServers/GdbRsp/GdbRspDebugServer.cpp index a512fe9a..81e2f2bd 100644 --- a/src/DebugServers/GdbRsp/GdbRspDebugServer.cpp +++ b/src/DebugServers/GdbRsp/GdbRspDebugServer.cpp @@ -12,457 +12,465 @@ #include "src/Exceptions/Exception.hpp" #include "src/Exceptions/InvalidConfig.hpp" -using namespace Bloom::DebugServers::Gdb; -using namespace Bloom::DebugServers::Gdb::CommandPackets; -using namespace Bloom::DebugServers::Gdb::ResponsePackets; -using namespace Bloom::DebugServers::Gdb::Exceptions; -using namespace Bloom::Events; -using namespace Bloom::Exceptions; +namespace Bloom::DebugServers::Gdb +{ + using namespace CommandPackets; + using namespace ResponsePackets; + using namespace Exceptions; + using namespace Bloom::Events; + using namespace Bloom::Exceptions; -using Bloom::Targets::TargetRegister; -using Bloom::Targets::TargetRegisterType; -using Bloom::Targets::TargetRegisterDescriptor; -using Bloom::Targets::TargetRegisterDescriptors; -using Bloom::Targets::TargetBreakpoint; + using Bloom::Targets::TargetRegister; + using Bloom::Targets::TargetRegisterType; + using Bloom::Targets::TargetRegisterDescriptor; + using Bloom::Targets::TargetRegisterDescriptors; + using Bloom::Targets::TargetBreakpoint; -void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) { - auto packetData = packet.getData(); - auto packetString = std::string(packetData.begin(), packetData.end()); + void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) { + auto packetData = packet.getData(); + auto packetString = std::string(packetData.begin(), packetData.end()); - if (packetString[0] == '?') { - // Status report - this->clientConnection->writePacket(TargetStopped(Signal::TRAP)); + if (packetString[0] == '?') { + // Status report + this->clientConnection->writePacket(TargetStopped(Signal::TRAP)); - } else if (packetString[0] == 'D') { - // Detach packet - there's not really anything we need to do here, so just respond with an OK - this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); + } else if (packetString[0] == 'D') { + // Detach packet - there's not really anything we need to do here, so just respond with an OK + this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); - } else if (packetString.find("qAttached") == 0) { - Logger::debug("Handling qAttached"); - this->clientConnection->writePacket(ResponsePacket({1})); - - } else { - Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response"); - - // Respond with an empty packet - this->clientConnection->writePacket(ResponsePacket({0})); - } -} - -void GdbRspDebugServer::handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet) { - Logger::debug("Handling QuerySupport packet"); - - if (!packet.isFeatureSupported(Feature::HARDWARE_BREAKPOINTS) - && !packet.isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS) - ) { - // All GDB clients are expected to support breakpoints! - throw ClientNotSupported("GDB client does not support HW or SW breakpoints"); - } - - // Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom - auto response = ResponsePackets::SupportedFeaturesResponse({ - {Feature::SOFTWARE_BREAKPOINTS, std::nullopt}, - {Feature::PACKET_SIZE, std::to_string(this->clientConnection->getMaxPacketSize())}, - }); - - this->clientConnection->writePacket(response); -} - -void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadRegisters& packet) { - Logger::debug("Handling ReadRegisters packet"); - - try { - auto descriptors = TargetRegisterDescriptors(); - - if (packet.registerNumber.has_value()) { - Logger::debug("Reading register number: " + std::to_string(packet.registerNumber.value())); - descriptors.insert(this->getTargetRegisterDescriptorFromNumber(packet.registerNumber.value())); + } else if (packetString.find("qAttached") == 0) { + Logger::debug("Handling qAttached"); + this->clientConnection->writePacket(ResponsePacket({1})); } else { - // Read all target registers mapped to a GDB register - for (const auto& descriptor : this->getRegisterNumberToDescriptorMapping().getMap()) { - descriptors.insert(this->getTargetRegisterDescriptorFromNumber(descriptor.second.number)); - } + Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response"); + + // Respond with an empty packet + this->clientConnection->writePacket(ResponsePacket({0})); } - - auto registerSet = this->targetControllerConsole.readRegisters(descriptors); - - /* - * Sort each register by their respective GDB register number - this will leave us with a collection of - * registers in the order expected by the GDB client. - */ - std::sort( - registerSet.begin(), - registerSet.end(), - [this] (const TargetRegister& registerA, const TargetRegister& registerB) { - return this->getRegisterNumberFromTargetRegisterDescriptor(registerA.descriptor) < - this->getRegisterNumberFromTargetRegisterDescriptor(registerB.descriptor); - } - ); - - /* - * Finally, reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure - * that each register value size matches the size in the associated GDB register descriptor, implode the - * values, convert to hexadecimal form and send to the GDB client. - */ - auto registers = std::vector(); - for (auto& reg : registerSet) { - std::reverse(reg.value.begin(), reg.value.end()); - - const auto gdbRegisterNumber = this->getRegisterNumberFromTargetRegisterDescriptor(reg.descriptor).value(); - const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(gdbRegisterNumber); - - if (reg.value.size() < gdbRegisterDescriptor.size) { - reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00); - } - - registers.insert(registers.end(), reg.value.begin(), reg.value.end()); - } - - auto responseRegisters = Packet::dataToHex(registers); - this->clientConnection->writePacket( - ResponsePacket(std::vector(responseRegisters.begin(), responseRegisters.end())) - ); - - } catch (const Exception& exception) { - Logger::error("Failed to read general registers - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); } -} -void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteRegister& packet) { - Logger::debug("Handling WriteRegister packet"); + void GdbRspDebugServer::handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet) { + Logger::debug("Handling QuerySupport packet"); - try { - auto targetRegisterDescriptor = this->getTargetRegisterDescriptorFromNumber(packet.registerNumber); - - const auto valueSize = packet.registerValue.size(); - if (valueSize > 0 && valueSize > targetRegisterDescriptor.size) { - // Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size. - for (auto i = packet.registerValue.size() - 1; i >= targetRegisterDescriptor.size; i--) { - if (packet.registerValue.at(i) != 0x00) { - // If we reach a non-zero byte, we cannot trim anymore without changing the data - break; - } - - packet.registerValue.erase(packet.registerValue.begin() + i); - } - - if (packet.registerValue.size() > targetRegisterDescriptor.size) { - const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(packet.registerNumber); - throw Exception("Cannot set value for " + gdbRegisterDescriptor.name - + " - value size exceeds register size." - ); - } + if (!packet.isFeatureSupported(Feature::HARDWARE_BREAKPOINTS) + && !packet.isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS) + ) { + // All GDB clients are expected to support breakpoints! + throw ClientNotSupported("GDB client does not support HW or SW breakpoints"); } - this->targetControllerConsole.writeRegisters({ - TargetRegister(targetRegisterDescriptor, packet.registerValue) + // Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom + auto response = ResponsePackets::SupportedFeaturesResponse({ + {Feature::SOFTWARE_BREAKPOINTS, std::nullopt}, + {Feature::PACKET_SIZE, std::to_string(this->clientConnection->getMaxPacketSize())}, }); - this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); - } catch (const Exception& exception) { - Logger::error("Failed to write registers - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } -} - -void GdbRspDebugServer::handleGdbPacket(CommandPackets::ContinueExecution& packet) { - Logger::debug("Handling ContinueExecution packet"); - - try { - this->targetControllerConsole.continueTargetExecution(packet.fromProgramCounter); - this->clientConnection->waitingForBreak = true; - - } catch (const Exception& exception) { - Logger::error("Failed to continue execution on target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } -} - -void GdbRspDebugServer::handleGdbPacket(CommandPackets::StepExecution& packet) { - Logger::debug("Handling StepExecution packet"); - - try { - this->targetControllerConsole.stepTargetExecution(packet.fromProgramCounter); - this->clientConnection->waitingForBreak = true; - - } catch (const Exception& exception) { - Logger::error("Failed to step execution on target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } -} - -void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadMemory& packet) { - Logger::debug("Handling ReadMemory packet"); - - try { - auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress); - auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress); - auto memoryBuffer = this->targetControllerConsole.readMemory(memoryType, startAddress, packet.bytes); - - auto hexMemoryBuffer = Packet::dataToHex(memoryBuffer); - this->clientConnection->writePacket( - ResponsePacket(std::vector(hexMemoryBuffer.begin(), hexMemoryBuffer.end())) - ); - - } catch (const Exception& exception) { - Logger::error("Failed to read memory from target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } -} - -void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteMemory& packet) { - Logger::debug("Handling WriteMemory packet"); - - try { - auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress); - auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress); - this->targetControllerConsole.writeMemory(memoryType, startAddress, packet.buffer); - - this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); - - } catch (const Exception& exception) { - Logger::error("Failed to write memory two target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } -} - -void GdbRspDebugServer::handleGdbPacket(CommandPackets::SetBreakpoint& packet) { - Logger::debug("Handling SetBreakpoint packet"); - - try { - auto breakpoint = TargetBreakpoint(); - breakpoint.address = packet.address; - this->targetControllerConsole.setBreakpoint(breakpoint); - - this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); - - } catch (const Exception& exception) { - Logger::error("Failed to set breakpoint on target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } -} - -void GdbRspDebugServer::handleGdbPacket(CommandPackets::RemoveBreakpoint& packet) { - Logger::debug("Removing breakpoint at address " + std::to_string(packet.address)); - - try { - auto breakpoint = TargetBreakpoint(); - breakpoint.address = packet.address; - this->targetControllerConsole.removeBreakpoint(breakpoint); - - this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); - - } catch (const Exception& exception) { - Logger::error("Failed to remove breakpoint on target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } -} - -void GdbRspDebugServer::handleGdbPacket(CommandPackets::InterruptExecution& packet) { - Logger::debug("Handling InterruptExecution packet"); - - try { - this->targetControllerConsole.stopTargetExecution(); - this->clientConnection->writePacket(TargetStopped(Signal::INTERRUPTED)); - this->clientConnection->waitingForBreak = false; - - } catch (const Exception& exception) { - Logger::error("Failed to interrupt execution - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } -} - -void GdbRspDebugServer::init() { - auto ipAddress = this->debugServerConfig.jsonObject.find("ipAddress")->toString().toStdString(); - auto configPortJsonValue = this->debugServerConfig.jsonObject.find("port"); - auto configPortValue = configPortJsonValue->isString() - ? static_cast(configPortJsonValue->toString().toInt(nullptr, 10)) - : static_cast(configPortJsonValue->toInt()); - - if (!ipAddress.empty()) { - this->listeningAddress = ipAddress; + this->clientConnection->writePacket(response); } - if (configPortValue > 0) { - this->listeningPortNumber = configPortValue; - } + void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadRegisters& packet) { + Logger::debug("Handling ReadRegisters packet"); - this->socketAddress.sin_family = AF_INET; - this->socketAddress.sin_port = htons(this->listeningPortNumber); + try { + auto descriptors = TargetRegisterDescriptors(); - if (::inet_pton(AF_INET, this->listeningAddress.c_str(), &(this->socketAddress.sin_addr)) == 0) { - // Invalid IP address - throw InvalidConfig("Invalid IP address provided in config file: (\"" + this->listeningAddress + "\")"); - } + if (packet.registerNumber.has_value()) { + Logger::debug("Reading register number: " + std::to_string(packet.registerNumber.value())); + descriptors.insert(this->getTargetRegisterDescriptorFromNumber(packet.registerNumber.value())); - int socketFileDescriptor; + } else { + // Read all target registers mapped to a GDB register + for (const auto& descriptor : this->getRegisterNumberToDescriptorMapping().getMap()) { + descriptors.insert(this->getTargetRegisterDescriptorFromNumber(descriptor.second.number)); + } + } - if ((socketFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) == 0) { - throw Exception("Failed to create socket file descriptor."); - } - - if (::setsockopt( - socketFileDescriptor, - SOL_SOCKET, - SO_REUSEADDR, - &(this->enableReuseAddressSocketOption), - sizeof(this->enableReuseAddressSocketOption) - ) < 0 - ) { - Logger::error("Failed to set socket SO_REUSEADDR option."); - } - - if (::bind( - socketFileDescriptor, - reinterpret_cast(&(this->socketAddress)), - sizeof(this->socketAddress) - ) < 0 - ) { - throw Exception("Failed to bind address. The selected port number (" - + std::to_string(this->listeningPortNumber) + ") may be in use."); - } - - this->serverSocketFileDescriptor = socketFileDescriptor; - - this->eventFileDescriptor = ::epoll_create(2); - struct epoll_event event = {}; - event.events = EPOLLIN; - event.data.fd = this->serverSocketFileDescriptor; - - if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->serverSocketFileDescriptor, &event) != 0) { - throw Exception("Failed epoll_ctl server socket"); - } - - if (this->interruptEventNotifier != nullptr) { - auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor(); - event.events = EPOLLIN; - event.data.fd = interruptFileDescriptor; - - if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) { - throw Exception("Failed epoll_ctl interrupt event fd"); - } - } - - Logger::info("GDB RSP address: " + this->listeningAddress); - Logger::info("GDB RSP port: " + std::to_string(this->listeningPortNumber)); - - this->eventListener->registerCallbackForEventType( - std::bind(&GdbRspDebugServer::onTargetControllerStateReported, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1) - ); -} - -void GdbRspDebugServer::close() { - this->closeClientConnection(); - - if (this->serverSocketFileDescriptor > 0) { - ::close(this->serverSocketFileDescriptor); - } -} - -void GdbRspDebugServer::serve() { - try { - if (!this->clientConnection.has_value()) { - Logger::info("Waiting for GDB RSP connection"); - - do { - this->waitForConnection(); - - } while (!this->clientConnection.has_value()); - - this->clientConnection->accept(this->serverSocketFileDescriptor); - Logger::info("Accepted GDP RSP connection from " + this->clientConnection->getIpAddress()); - this->eventManager.triggerEvent(std::make_shared()); + auto registerSet = this->targetControllerConsole.readRegisters(descriptors); /* - * Before proceeding with a new debug session, we must ensure that the TargetController is able to - * service it. + * Sort each register by their respective GDB register number - this will leave us with a collection of + * registers in the order expected by the GDB client. */ - if (!this->targetControllerConsole.isTargetControllerInService()) { - this->closeClientConnection(); - throw DebugSessionAborted("TargetController not in service"); + std::sort( + registerSet.begin(), + registerSet.end(), + [this](const TargetRegister& registerA, const TargetRegister& registerB) { + return this->getRegisterNumberFromTargetRegisterDescriptor(registerA.descriptor) < + this->getRegisterNumberFromTargetRegisterDescriptor(registerB.descriptor); + } + ); + + /* + * Finally, reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure + * that each register value size matches the size in the associated GDB register descriptor, implode the + * values, convert to hexadecimal form and send to the GDB client. + */ + auto registers = std::vector(); + for (auto& reg : registerSet) { + std::reverse(reg.value.begin(), reg.value.end()); + + const auto gdbRegisterNumber = this->getRegisterNumberFromTargetRegisterDescriptor( + reg.descriptor + ).value(); + const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(gdbRegisterNumber); + + if (reg.value.size() < gdbRegisterDescriptor.size) { + reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00); + } + + registers.insert(registers.end(), reg.value.begin(), reg.value.end()); + } + + auto responseRegisters = Packet::dataToHex(registers); + this->clientConnection->writePacket( + ResponsePacket(std::vector(responseRegisters.begin(), responseRegisters.end())) + ); + + } catch (const Exception& exception) { + Logger::error("Failed to read general registers - " + exception.getMessage()); + this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); + } + } + + void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteRegister& packet) { + Logger::debug("Handling WriteRegister packet"); + + try { + auto targetRegisterDescriptor = this->getTargetRegisterDescriptorFromNumber(packet.registerNumber); + + const auto valueSize = packet.registerValue.size(); + if (valueSize > 0 && valueSize > targetRegisterDescriptor.size) { + // Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size. + for (auto i = packet.registerValue.size() - 1; i >= targetRegisterDescriptor.size; i--) { + if (packet.registerValue.at(i) != 0x00) { + // If we reach a non-zero byte, we cannot trim anymore without changing the data + break; + } + + packet.registerValue.erase(packet.registerValue.begin() + i); + } + + if (packet.registerValue.size() > targetRegisterDescriptor.size) { + const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(packet.registerNumber); + throw Exception("Cannot set value for " + gdbRegisterDescriptor.name + + " - value size exceeds register size." + ); + } + } + + this->targetControllerConsole.writeRegisters({ + TargetRegister(targetRegisterDescriptor, packet.registerValue) + }); + this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); + + } catch (const Exception& exception) { + Logger::error("Failed to write registers - " + exception.getMessage()); + this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); + } + } + + void GdbRspDebugServer::handleGdbPacket(CommandPackets::ContinueExecution& packet) { + Logger::debug("Handling ContinueExecution packet"); + + try { + this->targetControllerConsole.continueTargetExecution(packet.fromProgramCounter); + this->clientConnection->waitingForBreak = true; + + } catch (const Exception& exception) { + Logger::error("Failed to continue execution on target - " + exception.getMessage()); + this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); + } + } + + void GdbRspDebugServer::handleGdbPacket(CommandPackets::StepExecution& packet) { + Logger::debug("Handling StepExecution packet"); + + try { + this->targetControllerConsole.stepTargetExecution(packet.fromProgramCounter); + this->clientConnection->waitingForBreak = true; + + } catch (const Exception& exception) { + Logger::error("Failed to step execution on target - " + exception.getMessage()); + this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); + } + } + + void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadMemory& packet) { + Logger::debug("Handling ReadMemory packet"); + + try { + auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress); + auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress); + auto memoryBuffer = this->targetControllerConsole.readMemory(memoryType, startAddress, packet.bytes); + + auto hexMemoryBuffer = Packet::dataToHex(memoryBuffer); + this->clientConnection->writePacket( + ResponsePacket(std::vector(hexMemoryBuffer.begin(), hexMemoryBuffer.end())) + ); + + } catch (const Exception& exception) { + Logger::error("Failed to read memory from target - " + exception.getMessage()); + this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); + } + } + + void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteMemory& packet) { + Logger::debug("Handling WriteMemory packet"); + + try { + auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress); + auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress); + this->targetControllerConsole.writeMemory(memoryType, startAddress, packet.buffer); + + this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); + + } catch (const Exception& exception) { + Logger::error("Failed to write memory two target - " + exception.getMessage()); + this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); + } + } + + void GdbRspDebugServer::handleGdbPacket(CommandPackets::SetBreakpoint& packet) { + Logger::debug("Handling SetBreakpoint packet"); + + try { + auto breakpoint = TargetBreakpoint(); + breakpoint.address = packet.address; + this->targetControllerConsole.setBreakpoint(breakpoint); + + this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); + + } catch (const Exception& exception) { + Logger::error("Failed to set breakpoint on target - " + exception.getMessage()); + this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); + } + } + + void GdbRspDebugServer::handleGdbPacket(CommandPackets::RemoveBreakpoint& packet) { + Logger::debug("Removing breakpoint at address " + std::to_string(packet.address)); + + try { + auto breakpoint = TargetBreakpoint(); + breakpoint.address = packet.address; + this->targetControllerConsole.removeBreakpoint(breakpoint); + + this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); + + } catch (const Exception& exception) { + Logger::error("Failed to remove breakpoint on target - " + exception.getMessage()); + this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); + } + } + + void GdbRspDebugServer::handleGdbPacket(CommandPackets::InterruptExecution& packet) { + Logger::debug("Handling InterruptExecution packet"); + + try { + this->targetControllerConsole.stopTargetExecution(); + this->clientConnection->writePacket(TargetStopped(Signal::INTERRUPTED)); + this->clientConnection->waitingForBreak = false; + + } catch (const Exception& exception) { + Logger::error("Failed to interrupt execution - " + exception.getMessage()); + this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); + } + } + + void GdbRspDebugServer::init() { + auto ipAddress = this->debugServerConfig.jsonObject.find("ipAddress")->toString().toStdString(); + auto configPortJsonValue = this->debugServerConfig.jsonObject.find("port"); + auto configPortValue = configPortJsonValue->isString() + ? static_cast(configPortJsonValue->toString().toInt(nullptr, 10)) + : static_cast(configPortJsonValue->toInt()); + + if (!ipAddress.empty()) { + this->listeningAddress = ipAddress; + } + + if (configPortValue > 0) { + this->listeningPortNumber = configPortValue; + } + + this->socketAddress.sin_family = AF_INET; + this->socketAddress.sin_port = htons(this->listeningPortNumber); + + if (::inet_pton(AF_INET, this->listeningAddress.c_str(), &(this->socketAddress.sin_addr)) == 0) { + // Invalid IP address + throw InvalidConfig( + "Invalid IP address provided in config file: (\"" + this->listeningAddress + "\")" + ); + } + + int socketFileDescriptor; + + if ((socketFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) == 0) { + throw Exception("Failed to create socket file descriptor."); + } + + if (::setsockopt( + socketFileDescriptor, + SOL_SOCKET, + SO_REUSEADDR, + &(this->enableReuseAddressSocketOption), + sizeof(this->enableReuseAddressSocketOption) + ) < 0 + ) { + Logger::error("Failed to set socket SO_REUSEADDR option."); + } + + if (::bind( + socketFileDescriptor, + reinterpret_cast(&(this->socketAddress)), + sizeof(this->socketAddress) + ) < 0 + ) { + throw Exception("Failed to bind address. The selected port number (" + + std::to_string(this->listeningPortNumber) + ") may be in use."); + } + + this->serverSocketFileDescriptor = socketFileDescriptor; + + this->eventFileDescriptor = ::epoll_create(2); + struct epoll_event event = {}; + event.events = EPOLLIN; + event.data.fd = this->serverSocketFileDescriptor; + + if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->serverSocketFileDescriptor, &event) != 0) { + throw Exception("Failed epoll_ctl server socket"); + } + + if (this->interruptEventNotifier != nullptr) { + auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor(); + event.events = EPOLLIN; + event.data.fd = interruptFileDescriptor; + + if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) { + throw Exception("Failed epoll_ctl interrupt event fd"); } } - auto packets = this->clientConnection->readPackets(); + Logger::info("GDB RSP address: " + this->listeningAddress); + Logger::info("GDB RSP port: " + std::to_string(this->listeningPortNumber)); - // Only process the last packet - any others will likely be duplicates from an impatient client - if (!packets.empty()) { - // Double-dispatch to appropriate handler - packets.back()->dispatchToHandler(*this); + this->eventListener->registerCallbackForEventType( + std::bind(&GdbRspDebugServer::onTargetControllerStateReported, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1) + ); + } + + void GdbRspDebugServer::close() { + this->closeClientConnection(); + + if (this->serverSocketFileDescriptor > 0) { + ::close(this->serverSocketFileDescriptor); } - - } catch (const ClientDisconnected&) { - Logger::info("GDB RSP client disconnected"); - this->closeClientConnection(); - return; - - } catch (const ClientCommunicationError& exception) { - Logger::error("GDB RSP client communication error - " + exception.getMessage() + " - closing connection"); - this->closeClientConnection(); - return; - - } catch (const ClientNotSupported& exception) { - Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection"); - this->closeClientConnection(); - return; - - } catch (const DebugSessionAborted& exception) { - Logger::warning("GDB debug session aborted - " + exception.getMessage()); - this->closeClientConnection(); - return; - - } catch (const DebugServerInterrupted&) { - // Server was interrupted - Logger::debug("GDB RSP interrupted"); - return; - } -} - -void GdbRspDebugServer::waitForConnection() { - if (::listen(this->serverSocketFileDescriptor, 3) != 0) { - throw Exception("Failed to listen on server socket"); } - constexpr int maxEvents = 5; - std::array events = {}; - int eventCount = ::epoll_wait( - this->eventFileDescriptor, - events.data(), - maxEvents, - -1 - ); + void GdbRspDebugServer::serve() { + try { + if (!this->clientConnection.has_value()) { + Logger::info("Waiting for GDB RSP connection"); - if (eventCount > 0) { - for (size_t i = 0; i < eventCount; i++) { - auto fileDescriptor = events.at(i).data.fd; + do { + this->waitForConnection(); - if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) { - // Interrupted - this->interruptEventNotifier->clear(); - throw DebugServerInterrupted(); + } while (!this->clientConnection.has_value()); + + this->clientConnection->accept(this->serverSocketFileDescriptor); + Logger::info("Accepted GDP RSP connection from " + this->clientConnection->getIpAddress()); + this->eventManager.triggerEvent(std::make_shared()); + + /* + * Before proceeding with a new debug session, we must ensure that the TargetController is able to + * service it. + */ + if (!this->targetControllerConsole.isTargetControllerInService()) { + this->closeClientConnection(); + throw DebugSessionAborted("TargetController not in service"); + } } + + auto packets = this->clientConnection->readPackets(); + + // Only process the last packet - any others will likely be duplicates from an impatient client + if (!packets.empty()) { + // Double-dispatch to appropriate handler + packets.back()->dispatchToHandler(*this); + } + + } catch (const ClientDisconnected&) { + Logger::info("GDB RSP client disconnected"); + this->closeClientConnection(); + return; + + } catch (const ClientCommunicationError& exception) { + Logger::error( + "GDB RSP client communication error - " + exception.getMessage() + " - closing connection" + ); + this->closeClientConnection(); + return; + + } catch (const ClientNotSupported& exception) { + Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection"); + this->closeClientConnection(); + return; + + } catch (const DebugSessionAborted& exception) { + Logger::warning("GDB debug session aborted - " + exception.getMessage()); + this->closeClientConnection(); + return; + + } catch (const DebugServerInterrupted&) { + // Server was interrupted + Logger::debug("GDB RSP interrupted"); + return; + } + } + + void GdbRspDebugServer::waitForConnection() { + if (::listen(this->serverSocketFileDescriptor, 3) != 0) { + throw Exception("Failed to listen on server socket"); } - this->clientConnection = Connection(this->interruptEventNotifier); - } -} + constexpr int maxEvents = 5; + std::array events = {}; + int eventCount = ::epoll_wait( + this->eventFileDescriptor, + events.data(), + maxEvents, + -1 + ); -void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) { - if (event.state == TargetControllerState::SUSPENDED && this->clientConnection.has_value()) { - Logger::warning("Terminating debug session - TargetController suspended unexpectedly"); - this->closeClientConnection(); - } -} + if (eventCount > 0) { + for (size_t i = 0; i < eventCount; i++) { + auto fileDescriptor = events.at(i).data.fd; -void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) { - if (this->clientConnection.has_value() && this->clientConnection->waitingForBreak) { - this->clientConnection->writePacket(TargetStopped(Signal::TRAP)); - this->clientConnection->waitingForBreak = false; + if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) { + // Interrupted + this->interruptEventNotifier->clear(); + throw DebugServerInterrupted(); + } + } + + this->clientConnection = Connection(this->interruptEventNotifier); + } + } + + void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) { + if (event.state == TargetControllerState::SUSPENDED && this->clientConnection.has_value()) { + Logger::warning("Terminating debug session - TargetController suspended unexpectedly"); + this->closeClientConnection(); + } + } + + void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) { + if (this->clientConnection.has_value() && this->clientConnection->waitingForBreak) { + this->clientConnection->writePacket(TargetStopped(Signal::TRAP)); + this->clientConnection->waitingForBreak = false; + } } } diff --git a/src/DebugServers/GdbRsp/ResponsePackets/SupportedFeaturesResponse.cpp b/src/DebugServers/GdbRsp/ResponsePackets/SupportedFeaturesResponse.cpp index 4a16adcf..17c30709 100644 --- a/src/DebugServers/GdbRsp/ResponsePackets/SupportedFeaturesResponse.cpp +++ b/src/DebugServers/GdbRsp/ResponsePackets/SupportedFeaturesResponse.cpp @@ -1,23 +1,25 @@ #include "SupportedFeaturesResponse.hpp" -using namespace Bloom::DebugServers::Gdb::ResponsePackets; +namespace Bloom::DebugServers::Gdb::ResponsePackets +{ + std::vector SupportedFeaturesResponse::getData() const { + std::string output = "qSupported:"; + auto gdbFeatureMapping = getGdbFeatureToNameMapping(); -std::vector SupportedFeaturesResponse::getData() const { - std::string output = "qSupported:"; - auto gdbFeatureMapping = getGdbFeatureToNameMapping(); + for (const auto& supportedFeature : this->supportedFeatures) { + auto featureString = gdbFeatureMapping.valueAt(supportedFeature.first); - for (const auto& supportedFeature : this->supportedFeatures) { - auto featureString = gdbFeatureMapping.valueAt(supportedFeature.first); + if (featureString.has_value()) { + if (supportedFeature.second.has_value()) { + output.append(featureString.value() + "=" + supportedFeature.second.value() + ";"); + + } else { + output.append(featureString.value() + "+;"); + } - if (featureString.has_value()) { - if (supportedFeature.second.has_value()) { - output.append(featureString.value() + "=" + supportedFeature.second.value() + ";"); - } else { - output.append(featureString.value() + "+;"); } - } - } - return std::vector(output.begin(), output.end()); + return std::vector(output.begin(), output.end()); + } } diff --git a/src/DebugToolDrivers/Microchip/AtmelICE/AtmelIce.cpp b/src/DebugToolDrivers/Microchip/AtmelICE/AtmelIce.cpp index 3cf81b68..4f1d8413 100644 --- a/src/DebugToolDrivers/Microchip/AtmelICE/AtmelIce.cpp +++ b/src/DebugToolDrivers/Microchip/AtmelICE/AtmelIce.cpp @@ -3,90 +3,98 @@ #include "src/TargetController/Exceptions/DeviceFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -using namespace Bloom::DebugToolDrivers; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers +{ + using namespace Protocols::CmsisDap::Edbg::Avr; + using namespace Bloom::Exceptions; -void AtmelIce::init() { - UsbDevice::init(); + void AtmelIce::init() { + UsbDevice::init(); - // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number - auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); - usbHidInterface.setNumber(0); - usbHidInterface.setLibUsbDevice(this->libUsbDevice); - usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); - usbHidInterface.setVendorId(this->vendorId); - usbHidInterface.setProductId(this->productId); + // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number + auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); + usbHidInterface.setNumber(0); + usbHidInterface.setLibUsbDevice(this->libUsbDevice); + usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); + usbHidInterface.setVendorId(this->vendorId); + usbHidInterface.setProductId(this->productId); - if (!usbHidInterface.isInitialised()) { - usbHidInterface.detachKernelDriver(); - this->setConfiguration(0); - usbHidInterface.init(); + if (!usbHidInterface.isInitialised()) { + usbHidInterface.detachKernelDriver(); + this->setConfiguration(0); + usbHidInterface.init(); + } + + /* + * The Atmel-ICE EDBG/CMSIS-DAP interface doesn't operate properly when sending commands too quickly. + * + * Because of this, we have to enforce a minimum time gap between commands. See comment + * in CmsisDapInterface class declaration for more info. + */ + this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + + // We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so. + if (!this->sessionStarted) { + this->startSession(); + } + + this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + this->setInitialised(true); } - /* - * The Atmel-ICE EDBG/CMSIS-DAP interface doesn't operate properly when sending commands too quickly. - * - * Because of this, we have to enforce a minimum time gap between commands. See comment - * in CmsisDapInterface class declaration for more info. - */ - this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + void AtmelIce::close() { + if (this->sessionStarted) { + this->endSession(); + } - // We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so. - if (!this->sessionStarted) { - this->startSession(); + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); } - this->edbgAvr8Interface = std::make_unique(this->edbgInterface); - this->setInitialised(true); -} + std::string AtmelIce::getSerialNumber() { + using namespace CommandFrames::Discovery; -void AtmelIce::close() { - if (this->sessionStarted) { - this->endSession(); - } - - this->getEdbgInterface().getUsbHidInterface().close(); - UsbDevice::close(); -} - -std::string AtmelIce::getSerialNumber() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER) - ); - - if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) { - throw DeviceInitializationFailure( - "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + Query(QueryContext::SERIAL_NUMBER) ); + + if (response.getResponseId() != ResponseId::OK) { + throw DeviceInitializationFailure( + "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + ); + } + + auto data = response.getPayloadData(); + return std::string(data.begin(), data.end()); } - auto data = response.getPayloadData(); - return std::string(data.begin(), data.end()); -} + void AtmelIce::startSession() { + using namespace CommandFrames::HouseKeeping; -void AtmelIce::startSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::StartSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + StartSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceInitializationFailure("Failed to start session with Atmel-ICE!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceInitializationFailure("Failed to start session with Atmel-ICE!"); + } + + this->sessionStarted = true; } - this->sessionStarted = true; -} + void AtmelIce::endSession() { + using namespace CommandFrames::HouseKeeping; -void AtmelIce::endSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::EndSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + EndSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceFailure("Failed to end session with Atmel-ICE!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceFailure("Failed to end session with Atmel-ICE!"); + } + + this->sessionStarted = false; } - - this->sessionStarted = false; } diff --git a/src/DebugToolDrivers/Microchip/CuriosityNano/CuriosityNano.cpp b/src/DebugToolDrivers/Microchip/CuriosityNano/CuriosityNano.cpp index 42e3451b..7d7f363a 100644 --- a/src/DebugToolDrivers/Microchip/CuriosityNano/CuriosityNano.cpp +++ b/src/DebugToolDrivers/Microchip/CuriosityNano/CuriosityNano.cpp @@ -3,82 +3,90 @@ #include "src/TargetController/Exceptions/DeviceFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -using namespace Bloom::DebugToolDrivers; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers +{ + using namespace Protocols::CmsisDap::Edbg::Avr; + using namespace Bloom::Exceptions; -void CuriosityNano::init() { - UsbDevice::init(); + void CuriosityNano::init() { + UsbDevice::init(); - // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number - auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); - usbHidInterface.setNumber(0); - usbHidInterface.setLibUsbDevice(this->libUsbDevice); - usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); - usbHidInterface.setVendorId(this->vendorId); - usbHidInterface.setProductId(this->productId); + // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number + auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); + usbHidInterface.setNumber(0); + usbHidInterface.setLibUsbDevice(this->libUsbDevice); + usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); + usbHidInterface.setVendorId(this->vendorId); + usbHidInterface.setProductId(this->productId); - if (!usbHidInterface.isInitialised()) { - usbHidInterface.detachKernelDriver(); - usbHidInterface.init(); + if (!usbHidInterface.isInitialised()) { + usbHidInterface.detachKernelDriver(); + usbHidInterface.init(); + } + + this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + + if (!this->sessionStarted) { + this->startSession(); + } + + this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + this->setInitialised(true); } - this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + void CuriosityNano::close() { + if (this->sessionStarted) { + this->endSession(); + } - if (!this->sessionStarted) { - this->startSession(); + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); } - this->edbgAvr8Interface = std::make_unique(this->edbgInterface); - this->setInitialised(true); -} + std::string CuriosityNano::getSerialNumber() { + using namespace CommandFrames::Discovery; -void CuriosityNano::close() { - if (this->sessionStarted) { - this->endSession(); - } - - this->getEdbgInterface().getUsbHidInterface().close(); - UsbDevice::close(); -} - -std::string CuriosityNano::getSerialNumber() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER) - ); - - if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) { - throw DeviceInitializationFailure( - "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + Query(QueryContext::SERIAL_NUMBER) ); + + if (response.getResponseId() != ResponseId::OK) { + throw DeviceInitializationFailure( + "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + ); + } + + auto data = response.getPayloadData(); + return std::string(data.begin(), data.end()); } - auto data = response.getPayloadData(); - return std::string(data.begin(), data.end()); -} + void CuriosityNano::startSession() { + using namespace CommandFrames::HouseKeeping; -void CuriosityNano::startSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::StartSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + StartSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceInitializationFailure("Failed to start session with Curiosity Nano!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceInitializationFailure("Failed to start session with Curiosity Nano!"); + } + + this->sessionStarted = true; } - this->sessionStarted = true; -} + void CuriosityNano::endSession() { + using namespace CommandFrames::HouseKeeping; -void CuriosityNano::endSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::EndSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + EndSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceFailure("Failed to end session with Curiosity Nano!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceFailure("Failed to end session with Curiosity Nano!"); + } + + this->sessionStarted = false; } - - this->sessionStarted = false; } diff --git a/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp index 6ded86e1..542b3b96 100644 --- a/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp +++ b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp @@ -3,83 +3,91 @@ #include "src/TargetController/Exceptions/DeviceFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -using namespace Bloom::DebugToolDrivers; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers +{ + using namespace Protocols::CmsisDap::Edbg::Avr; + using namespace Bloom::Exceptions; -void MplabPickit4::init() { - UsbDevice::init(); + void MplabPickit4::init() { + UsbDevice::init(); - // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number - auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); - usbHidInterface.setNumber(0); - usbHidInterface.setLibUsbDevice(this->libUsbDevice); - usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); - usbHidInterface.setVendorId(this->vendorId); - usbHidInterface.setProductId(this->productId); + // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number + auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); + usbHidInterface.setNumber(0); + usbHidInterface.setLibUsbDevice(this->libUsbDevice); + usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); + usbHidInterface.setVendorId(this->vendorId); + usbHidInterface.setProductId(this->productId); - if (!usbHidInterface.isInitialised()) { - usbHidInterface.init(); + if (!usbHidInterface.isInitialised()) { + usbHidInterface.init(); + } + + this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + + // We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so. + if (!this->sessionStarted) { + this->startSession(); + } + + this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + + this->setInitialised(true); } - this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + void MplabPickit4::close() { + if (this->sessionStarted) { + this->endSession(); + } - // We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so. - if (!this->sessionStarted) { - this->startSession(); + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); } - this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + std::string MplabPickit4::getSerialNumber() { + using namespace CommandFrames::Discovery; - this->setInitialised(true); -} - -void MplabPickit4::close() { - if (this->sessionStarted) { - this->endSession(); - } - - this->getEdbgInterface().getUsbHidInterface().close(); - UsbDevice::close(); -} - -std::string MplabPickit4::getSerialNumber() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER) - ); - - if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) { - throw DeviceInitializationFailure( - "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + Query(QueryContext::SERIAL_NUMBER) ); + + if (response.getResponseId() != ResponseId::OK) { + throw DeviceInitializationFailure( + "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + ); + } + + auto data = response.getPayloadData(); + return std::string(data.begin(), data.end()); } - auto data = response.getPayloadData(); - return std::string(data.begin(), data.end()); -} + void MplabPickit4::startSession() { + using namespace CommandFrames::HouseKeeping; -void MplabPickit4::startSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::StartSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + StartSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceInitializationFailure("Failed to start session with MPLAB PICkit 4!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceInitializationFailure("Failed to start session with MPLAB PICkit 4!"); + } + + this->sessionStarted = true; } - this->sessionStarted = true; -} + void MplabPickit4::endSession() { + using namespace CommandFrames::HouseKeeping; -void MplabPickit4::endSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::EndSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + EndSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceFailure("Failed to end session with MPLAB PICkit 4!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceFailure("Failed to end session with MPLAB PICkit 4!"); + } + + this->sessionStarted = false; } - - this->sessionStarted = false; } diff --git a/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp index c87000db..60783f76 100644 --- a/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp +++ b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp @@ -3,82 +3,90 @@ #include "src/TargetController/Exceptions/DeviceFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -using namespace Bloom::DebugToolDrivers; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers +{ + using namespace Protocols::CmsisDap::Edbg::Avr; + using namespace Bloom::Exceptions; -void MplabSnap::init() { - UsbDevice::init(); + void MplabSnap::init() { + UsbDevice::init(); - // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number - auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); - usbHidInterface.setNumber(0); - usbHidInterface.setLibUsbDevice(this->libUsbDevice); - usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); - usbHidInterface.setVendorId(this->vendorId); - usbHidInterface.setProductId(this->productId); + // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number + auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); + usbHidInterface.setNumber(0); + usbHidInterface.setLibUsbDevice(this->libUsbDevice); + usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); + usbHidInterface.setVendorId(this->vendorId); + usbHidInterface.setProductId(this->productId); - if (!usbHidInterface.isInitialised()) { - usbHidInterface.init(); + if (!usbHidInterface.isInitialised()) { + usbHidInterface.init(); + } + + this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + + // We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so. + if (!this->sessionStarted) { + this->startSession(); + } + + this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + this->setInitialised(true); } - this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + void MplabSnap::close() { + if (this->sessionStarted) { + this->endSession(); + } - // We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so. - if (!this->sessionStarted) { - this->startSession(); + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); } - this->edbgAvr8Interface = std::make_unique(this->edbgInterface); - this->setInitialised(true); -} + std::string MplabSnap::getSerialNumber() { + using namespace CommandFrames::Discovery; -void MplabSnap::close() { - if (this->sessionStarted) { - this->endSession(); - } - - this->getEdbgInterface().getUsbHidInterface().close(); - UsbDevice::close(); -} - -std::string MplabSnap::getSerialNumber() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER) - ); - - if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) { - throw DeviceInitializationFailure( - "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + Query(QueryContext::SERIAL_NUMBER) ); + + if (response.getResponseId() != ResponseId::OK) { + throw DeviceInitializationFailure( + "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + ); + } + + auto data = response.getPayloadData(); + return std::string(data.begin(), data.end()); } - auto data = response.getPayloadData(); - return std::string(data.begin(), data.end()); -} + void MplabSnap::startSession() { + using namespace CommandFrames::HouseKeeping; -void MplabSnap::startSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::StartSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + StartSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceInitializationFailure("Failed to start session with MPLAB Snap!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceInitializationFailure("Failed to start session with MPLAB Snap!"); + } + + this->sessionStarted = true; } - this->sessionStarted = true; -} + void MplabSnap::endSession() { + using namespace CommandFrames::HouseKeeping; -void MplabSnap::endSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::EndSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + EndSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceFailure("Failed to end session with MPLAB Snap!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceFailure("Failed to end session with MPLAB Snap!"); + } + + this->sessionStarted = false; } - - this->sessionStarted = false; } diff --git a/src/DebugToolDrivers/Microchip/PowerDebugger/PowerDebugger.cpp b/src/DebugToolDrivers/Microchip/PowerDebugger/PowerDebugger.cpp index e0730fd0..b370f142 100644 --- a/src/DebugToolDrivers/Microchip/PowerDebugger/PowerDebugger.cpp +++ b/src/DebugToolDrivers/Microchip/PowerDebugger/PowerDebugger.cpp @@ -3,92 +3,100 @@ #include "src/TargetController/Exceptions/DeviceFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -using namespace Bloom::DebugToolDrivers; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers +{ + using namespace Protocols::CmsisDap::Edbg::Avr; + using namespace Bloom::Exceptions; -using Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::EdbgAvr8Interface; + void PowerDebugger::init() { + UsbDevice::init(); -void PowerDebugger::init() { - UsbDevice::init(); + // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number + auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); + usbHidInterface.setNumber(0); + usbHidInterface.setLibUsbDevice(this->libUsbDevice); + usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); + usbHidInterface.setVendorId(this->vendorId); + usbHidInterface.setProductId(this->productId); - // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number - auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); - usbHidInterface.setNumber(0); - usbHidInterface.setLibUsbDevice(this->libUsbDevice); - usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); - usbHidInterface.setVendorId(this->vendorId); - usbHidInterface.setProductId(this->productId); + if (!usbHidInterface.isInitialised()) { + usbHidInterface.init(); + } - if (!usbHidInterface.isInitialised()) { - usbHidInterface.init(); + /* + * The Power Debugger EDBG/CMSIS-DAP interface doesn't operate properly when sending commands too quickly. + * + * Because of this, we have to enforce a minimum time gap between commands. See comment in + * CmsisDapInterface class declaration for more info. + */ + this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + + // We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so. + if (!this->sessionStarted) { + this->startSession(); + } + + this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + this->setInitialised(true); } - /* - * The Power Debugger EDBG/CMSIS-DAP interface doesn't operate properly when sending commands too quickly. - * - * Because of this, we have to enforce a minimum time gap between commands. See comment in - * CmsisDapInterface class declaration for more info. - */ - this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + void PowerDebugger::close() { + if (this->sessionStarted) { + this->endSession(); + } - // We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so. - if (!this->sessionStarted) { - this->startSession(); + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); } - this->edbgAvr8Interface = std::make_unique(this->edbgInterface); - this->setInitialised(true); -} + std::string PowerDebugger::getSerialNumber() { + using namespace CommandFrames::Discovery; -void PowerDebugger::close() { - if (this->sessionStarted) { - this->endSession(); - } - - this->getEdbgInterface().getUsbHidInterface().close(); - UsbDevice::close(); -} - -std::string PowerDebugger::getSerialNumber() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER) - ); - - if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) { - throw DeviceInitializationFailure( - "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + Query(QueryContext::SERIAL_NUMBER) ); + + if (response.getResponseId() != ResponseId::OK) { + throw DeviceInitializationFailure( + "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + ); + } + + auto data = response.getPayloadData(); + return std::string(data.begin(), data.end()); } - auto data = response.getPayloadData(); - return std::string(data.begin(), data.end()); -} + void PowerDebugger::startSession() { + using namespace CommandFrames::HouseKeeping; -void PowerDebugger::startSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::StartSession() - ); - - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceInitializationFailure( - "Failed to start session with the Power Debugger - device returned failed response ID" + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + StartSession() ); + + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceInitializationFailure( + "Failed to start session with the Power Debugger - device returned failed response ID" + ); + } + + this->sessionStarted = true; } - this->sessionStarted = true; -} + void PowerDebugger::endSession() { + using namespace CommandFrames::HouseKeeping; -void PowerDebugger::endSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::EndSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + EndSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceFailure("Failed to end session with the Power Debugger - device returned failed response ID"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceFailure( + "Failed to end session with the Power Debugger - device returned failed response ID" + ); + } + + this->sessionStarted = false; } - - this->sessionStarted = false; } diff --git a/src/DebugToolDrivers/Microchip/XplainedMini/XplainedMini.cpp b/src/DebugToolDrivers/Microchip/XplainedMini/XplainedMini.cpp index aa54c8d2..140d39b7 100644 --- a/src/DebugToolDrivers/Microchip/XplainedMini/XplainedMini.cpp +++ b/src/DebugToolDrivers/Microchip/XplainedMini/XplainedMini.cpp @@ -3,82 +3,90 @@ #include "src/TargetController/Exceptions/DeviceFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -using namespace Bloom::DebugToolDrivers; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers +{ + using namespace Protocols::CmsisDap::Edbg::Avr; + using namespace Bloom::Exceptions; -void XplainedMini::init() { - UsbDevice::init(); + void XplainedMini::init() { + UsbDevice::init(); - // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number - auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); - usbHidInterface.setNumber(0); - usbHidInterface.setLibUsbDevice(this->libUsbDevice); - usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); - usbHidInterface.setVendorId(this->vendorId); - usbHidInterface.setProductId(this->productId); + // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number + auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); + usbHidInterface.setNumber(0); + usbHidInterface.setLibUsbDevice(this->libUsbDevice); + usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); + usbHidInterface.setVendorId(this->vendorId); + usbHidInterface.setProductId(this->productId); - if (!usbHidInterface.isInitialised()) { - usbHidInterface.detachKernelDriver(); - usbHidInterface.init(); + if (!usbHidInterface.isInitialised()) { + usbHidInterface.detachKernelDriver(); + usbHidInterface.init(); + } + + this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + + if (!this->sessionStarted) { + this->startSession(); + } + + this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + this->setInitialised(true); } - this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + void XplainedMini::close() { + if (this->sessionStarted) { + this->endSession(); + } - if (!this->sessionStarted) { - this->startSession(); + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); } - this->edbgAvr8Interface = std::make_unique(this->edbgInterface); - this->setInitialised(true); -} + std::string XplainedMini::getSerialNumber() { + using namespace CommandFrames::Discovery; -void XplainedMini::close() { - if (this->sessionStarted) { - this->endSession(); - } - - this->getEdbgInterface().getUsbHidInterface().close(); - UsbDevice::close(); -} - -std::string XplainedMini::getSerialNumber() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER) - ); - - if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) { - throw DeviceInitializationFailure( - "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + Query(QueryContext::SERIAL_NUMBER) ); + + if (response.getResponseId() != ResponseId::OK) { + throw DeviceInitializationFailure( + "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + ); + } + + auto data = response.getPayloadData(); + return std::string(data.begin(), data.end()); } - auto data = response.getPayloadData(); - return std::string(data.begin(), data.end()); -} + void XplainedMini::startSession() { + using namespace CommandFrames::HouseKeeping; -void XplainedMini::startSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::StartSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + StartSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceInitializationFailure("Failed to start session with Xplained Mini!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceInitializationFailure("Failed to start session with Xplained Mini!"); + } + + this->sessionStarted = true; } - this->sessionStarted = true; -} + void XplainedMini::endSession() { + using namespace CommandFrames::HouseKeeping; -void XplainedMini::endSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::EndSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + EndSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceFailure("Failed to end session with Xplained Mini!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceFailure("Failed to end session with Xplained Mini!"); + } + + this->sessionStarted = false; } - - this->sessionStarted = false; } diff --git a/src/DebugToolDrivers/Microchip/XplainedNano/XplainedNano.cpp b/src/DebugToolDrivers/Microchip/XplainedNano/XplainedNano.cpp index 96b1695b..d9ef241d 100644 --- a/src/DebugToolDrivers/Microchip/XplainedNano/XplainedNano.cpp +++ b/src/DebugToolDrivers/Microchip/XplainedNano/XplainedNano.cpp @@ -3,82 +3,90 @@ #include "src/TargetController/Exceptions/DeviceFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -using namespace Bloom::DebugToolDrivers; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers +{ + using namespace Protocols::CmsisDap::Edbg::Avr; + using namespace Bloom::Exceptions; -void XplainedNano::init() { - UsbDevice::init(); + void XplainedNano::init() { + UsbDevice::init(); - // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number - auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); - usbHidInterface.setNumber(0); - usbHidInterface.setLibUsbDevice(this->libUsbDevice); - usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); - usbHidInterface.setVendorId(this->vendorId); - usbHidInterface.setProductId(this->productId); + // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number + auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); + usbHidInterface.setNumber(0); + usbHidInterface.setLibUsbDevice(this->libUsbDevice); + usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); + usbHidInterface.setVendorId(this->vendorId); + usbHidInterface.setProductId(this->productId); - if (!usbHidInterface.isInitialised()) { - usbHidInterface.detachKernelDriver(); - usbHidInterface.init(); + if (!usbHidInterface.isInitialised()) { + usbHidInterface.detachKernelDriver(); + usbHidInterface.init(); + } + + this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + + if (!this->sessionStarted) { + this->startSession(); + } + + this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + this->setInitialised(true); } - this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + void XplainedNano::close() { + if (this->sessionStarted) { + this->endSession(); + } - if (!this->sessionStarted) { - this->startSession(); + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); } - this->edbgAvr8Interface = std::make_unique(this->edbgInterface); - this->setInitialised(true); -} + std::string XplainedNano::getSerialNumber() { + using namespace CommandFrames::Discovery; -void XplainedNano::close() { - if (this->sessionStarted) { - this->endSession(); - } - - this->getEdbgInterface().getUsbHidInterface().close(); - UsbDevice::close(); -} - -std::string XplainedNano::getSerialNumber() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER) - ); - - if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) { - throw DeviceInitializationFailure( - "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + Query(QueryContext::SERIAL_NUMBER) ); + + if (response.getResponseId() != ResponseId::OK) { + throw DeviceInitializationFailure( + "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + ); + } + + auto data = response.getPayloadData(); + return std::string(data.begin(), data.end()); } - auto data = response.getPayloadData(); - return std::string(data.begin(), data.end()); -} + void XplainedNano::startSession() { + using namespace CommandFrames::HouseKeeping; -void XplainedNano::startSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::StartSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + StartSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceInitializationFailure("Failed to start session with Xplained Nano!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceInitializationFailure("Failed to start session with Xplained Nano!"); + } + + this->sessionStarted = true; } - this->sessionStarted = true; -} + void XplainedNano::endSession() { + using namespace CommandFrames::HouseKeeping; -void XplainedNano::endSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::EndSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + EndSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceFailure("Failed to end session with Xplained Nano!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceFailure("Failed to end session with Xplained Nano!"); + } + + this->sessionStarted = false; } - - this->sessionStarted = false; } diff --git a/src/DebugToolDrivers/Microchip/XplainedPro/XplainedPro.cpp b/src/DebugToolDrivers/Microchip/XplainedPro/XplainedPro.cpp index 7ced9319..ad2965e0 100644 --- a/src/DebugToolDrivers/Microchip/XplainedPro/XplainedPro.cpp +++ b/src/DebugToolDrivers/Microchip/XplainedPro/XplainedPro.cpp @@ -3,91 +3,99 @@ #include "src/TargetController/Exceptions/DeviceFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -using namespace Bloom::DebugToolDrivers; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers +{ + using namespace Protocols::CmsisDap::Edbg::Avr; + using namespace Bloom::Exceptions; -void XplainedPro::init() { - UsbDevice::init(); + void XplainedPro::init() { + UsbDevice::init(); - // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number - auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); - usbHidInterface.setNumber(0); - usbHidInterface.setLibUsbDevice(this->libUsbDevice); - usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); - usbHidInterface.setVendorId(this->vendorId); - usbHidInterface.setProductId(this->productId); + // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number + auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); + usbHidInterface.setNumber(0); + usbHidInterface.setLibUsbDevice(this->libUsbDevice); + usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); + usbHidInterface.setVendorId(this->vendorId); + usbHidInterface.setProductId(this->productId); - if (!usbHidInterface.isInitialised()) { - usbHidInterface.detachKernelDriver(); - usbHidInterface.init(); + if (!usbHidInterface.isInitialised()) { + usbHidInterface.detachKernelDriver(); + usbHidInterface.init(); + } + + this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + + if (!this->sessionStarted) { + this->startSession(); + } + + this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + + /* + * The Xplained Pro debug tool returns incorrect data for any read memory command that exceeds 256 bytes in the + * size of the read request, despite the fact that the HID report size is 512 bytes. The debug tool doesn't + * report any error, it just returns incorrect data. + * + * This means we must enforce a hard limit on the number of bytes we attempt to access, per request. + */ + this->edbgAvr8Interface->setMaximumMemoryAccessSizePerRequest(256); + this->setInitialised(true); } - this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + void XplainedPro::close() { + if (this->sessionStarted) { + this->endSession(); + } - if (!this->sessionStarted) { - this->startSession(); + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); } - this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + std::string XplainedPro::getSerialNumber() { + using namespace CommandFrames::Discovery; - /* - * The Xplained Pro debug tool returns incorrect data for any read memory command that exceeds 256 bytes in the - * size of the read request, despite the fact that the HID report size is 512 bytes. The debug tool doesn't - * report any error, it just returns incorrect data. - * - * This means we must enforce a hard limit on the number of bytes we attempt to access, per request. - */ - this->edbgAvr8Interface->setMaximumMemoryAccessSizePerRequest(256); - this->setInitialised(true); -} - -void XplainedPro::close() { - if (this->sessionStarted) { - this->endSession(); - } - - this->getEdbgInterface().getUsbHidInterface().close(); - UsbDevice::close(); -} - -std::string XplainedPro::getSerialNumber() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER) - ); - - if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) { - throw DeviceInitializationFailure( - "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + Query(QueryContext::SERIAL_NUMBER) ); + + if (response.getResponseId() != ResponseId::OK) { + throw DeviceInitializationFailure( + "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + ); + } + + auto data = response.getPayloadData(); + return std::string(data.begin(), data.end()); } - auto data = response.getPayloadData(); - return std::string(data.begin(), data.end()); -} + void XplainedPro::startSession() { + using namespace CommandFrames::HouseKeeping; -void XplainedPro::startSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::StartSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + StartSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceInitializationFailure("Failed to start session with Xplained Pro!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceInitializationFailure("Failed to start session with Xplained Pro!"); + } + + this->sessionStarted = true; } - this->sessionStarted = true; -} + void XplainedPro::endSession() { + using namespace CommandFrames::HouseKeeping; -void XplainedPro::endSession() { - auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( - CommandFrames::HouseKeeping::EndSession() - ); + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + EndSession() + ); - if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { - // Failed response returned! - throw DeviceFailure("Failed to end session with Xplained Pro!"); + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceFailure("Failed to end session with Xplained Pro!"); + } + + this->sessionStarted = false; } - - this->sessionStarted = false; } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.cpp index b3421fc3..54f5e17b 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.cpp @@ -6,45 +6,47 @@ #include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.hpp" #include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp" -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap +{ + using namespace Bloom::Exceptions; -void CmsisDapInterface::sendCommand(const Command& cmsisDapCommand) { - if (this->msSendCommandDelay.count() > 0) { - using namespace std::chrono; - std::int64_t now = duration_cast(high_resolution_clock::now().time_since_epoch()).count(); - std::int64_t difference = (now - this->lastCommandSentTimeStamp); + void CmsisDapInterface::sendCommand(const Command& cmsisDapCommand) { + if (this->msSendCommandDelay.count() > 0) { + using namespace std::chrono; + std::int64_t now = duration_cast(high_resolution_clock::now().time_since_epoch()).count(); + std::int64_t difference = (now - this->lastCommandSentTimeStamp); - if (difference < this->msSendCommandDelay.count()) { - std::this_thread::sleep_for(milliseconds(this->msSendCommandDelay.count() - difference)); + if (difference < this->msSendCommandDelay.count()) { + std::this_thread::sleep_for(milliseconds(this->msSendCommandDelay.count() - difference)); + } + + this->lastCommandSentTimeStamp = now; } - this->lastCommandSentTimeStamp = now; + this->getUsbHidInterface().write(static_cast>(cmsisDapCommand)); } - this->getUsbHidInterface().write(static_cast>(cmsisDapCommand)); -} + std::unique_ptr CmsisDapInterface::getResponse() { + auto rawResponse = this->getUsbHidInterface().read(10000); -std::unique_ptr CmsisDapInterface::getResponse() { - auto rawResponse = this->getUsbHidInterface().read(10000); + if (rawResponse.empty()) { + throw DeviceCommunicationFailure("Empty CMSIS-DAP response received"); + } - if (rawResponse.empty()) { - throw DeviceCommunicationFailure("Empty CMSIS-DAP response received"); + auto response = std::make_unique(Response()); + response->init(rawResponse); + return response; } - auto response = std::make_unique(Response()); - response->init(rawResponse); - return response; -} + std::unique_ptr CmsisDapInterface::sendCommandAndWaitForResponse(const Command& cmsisDapCommand) { + this->sendCommand(cmsisDapCommand); + auto response = this->getResponse(); -std::unique_ptr CmsisDapInterface::sendCommandAndWaitForResponse(const Command& cmsisDapCommand) { - this->sendCommand(cmsisDapCommand); - auto response = this->getResponse(); + if (response->getResponseId() != cmsisDapCommand.getCommandId()) { + // This response is not what we were expecting + throw DeviceCommunicationFailure("Unexpected response to CMSIS-DAP command."); + } - if (response->getResponseId() != cmsisDapCommand.getCommandId()) { - // This response is not what we were expecting - throw DeviceCommunicationFailure("Unexpected response to CMSIS-DAP command."); + return response; } - - return response; } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.cpp index b5070f87..ad81ef51 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.cpp @@ -1,11 +1,12 @@ #include "Command.hpp" -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap +{ + Command::operator std::vector() const { + auto rawCommand = std::vector(1, this->getCommandId()); + auto commandData = this->getData(); + rawCommand.insert(rawCommand.end(), commandData.begin(), commandData.end()); -Command::operator std::vector () const { - auto rawCommand = std::vector(1, this->getCommandId()); - auto commandData = this->getData(); - rawCommand.insert(rawCommand.end(), commandData.begin(), commandData.end()); - - return rawCommand; + return rawCommand; + } } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.cpp index d2af235c..b5b90412 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.cpp @@ -2,13 +2,14 @@ #include "src/Exceptions/Exception.hpp" -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap +{ + void Response::init(const std::vector& rawResponse) { + if (rawResponse.empty()) { + throw Exceptions::Exception("Failed to process CMSIS-DAP response - invalid response"); + } -void Response::init(const std::vector& rawResponse) { - if (rawResponse.empty()) { - throw Exceptions::Exception("Failed to process CMSIS-DAP response - invalid response"); + this->setResponseId(rawResponse[0]); + this->setData(std::vector(rawResponse.begin() + 1, rawResponse.end())); } - - this->setResponseId(rawResponse[0]); - this->setData(std::vector(rawResponse.begin() + 1, rawResponse.end())); } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrCommand.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrCommand.cpp index 87c838a3..61ce4975 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrCommand.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrCommand.cpp @@ -1,22 +1,23 @@ #include "AvrCommand.hpp" -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr +{ + std::vector AvrCommand::getData() const { + std::vector data; + auto commandPacket = this->getCommandPacket(); + std::size_t commandPacketSize = commandPacket.size(); + data.resize(3 + commandPacketSize); + // FragmentInfo byte + data[0] = static_cast((this->getFragmentNumber() << 4) | this->getFragmentCount()); -std::vector AvrCommand::getData() const { - std::vector data; - auto commandPacket = this->getCommandPacket(); - std::size_t commandPacketSize = commandPacket.size(); - data.resize(3 + commandPacketSize); - // FragmentInfo byte - data[0] = static_cast((this->getFragmentNumber() << 4) | this->getFragmentCount()); + // Size byte + data[1] = static_cast(commandPacketSize >> 8); + data[2] = static_cast(commandPacketSize & 0xFF); - // Size byte - data[1] = (unsigned char) (commandPacketSize >> 8); - data[2] = (unsigned char) (commandPacketSize & 0xFF); + if (commandPacketSize > 0) { + data.insert(data.begin() + 3, commandPacket.begin(), commandPacket.end()); + } - if (commandPacketSize > 0) { - data.insert(data.begin() + 3, commandPacket.begin(), commandPacket.end()); + return data; } - - return data; } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrEvent.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrEvent.cpp index a13cdd41..bc70e8fc 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrEvent.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrEvent.cpp @@ -2,48 +2,50 @@ #include "src/Exceptions/Exception.hpp" -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr +{ + using namespace Bloom::Exceptions; -void AvrEvent::init(const std::vector& rawResponse) { - Response::init(rawResponse); + void AvrEvent::init(const std::vector& rawResponse) { + Response::init(rawResponse); - if (this->getResponseId() != 0x82) { - throw Exception("Failed to construct AvrEvent object - invalid response ID."); - } + if (this->getResponseId() != 0x82) { + throw Exception("Failed to construct AvrEvent object - invalid response ID."); + } - auto& responseData = this->getData(); + const auto& responseData = this->getData(); - if (responseData.size() < 2) { - // All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID) - throw Exception("Failed to construct AvrEvent object - AVR_EVT response " - "returned no additional data."); - } + if (responseData.size() < 2) { + // All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID) + throw Exception("Failed to construct AvrEvent object - AVR_EVT response " + "returned no additional data."); + } - // Response size is two bytes, MSB - auto responsePacketSize = static_cast((responseData[0] << 8) | responseData[1]); + // Response size is two bytes, MSB + auto responsePacketSize = static_cast((responseData[0] << 8) | responseData[1]); - if (responseData.size() < 2) { - // All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID) - throw Exception("Failed to construct AvrEvent object - AVR_EVT response " - "contained invalid event data size."); - } + if (responseData.size() < 2) { + // All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID) + throw Exception("Failed to construct AvrEvent object - AVR_EVT response " + "contained invalid event data size."); + } - auto eventData = std::vector(); + auto eventData = std::vector(); - /* - * Ignore the SOF, protocol version &handler id and sequence ID (with all make up 5 bytes in total, 7 when - * you include the two size bytes) - */ - eventData.insert( - eventData.end(), - responseData.begin() + 7, - responseData.begin() + 7 + static_cast(responsePacketSize) - ); + /* + * Ignore the SOF, protocol version &handler id and sequence ID (with all make up 5 bytes in total, 7 when + * you include the two size bytes) + */ + eventData.insert( + eventData.end(), + responseData.begin() + 7, + responseData.begin() + 7 + static_cast(responsePacketSize) + ); - this->setEventData(eventData); + this->setEventData(eventData); - if (!eventData.empty()) { - this->eventId = eventData[0]; + if (!eventData.empty()) { + this->eventId = eventData[0]; + } } } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrResponse.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrResponse.cpp index 60cf7d2e..ba076932 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrResponse.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/AvrResponse.cpp @@ -2,40 +2,42 @@ #include "src/Exceptions/Exception.hpp" -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr +{ + using namespace Bloom::Exceptions; -void AvrResponse::init(const std::vector& rawResponse) { - Response::init(rawResponse); + void AvrResponse::init(const std::vector& rawResponse) { + Response::init(rawResponse); - if (this->getResponseId() != 0x81) { - throw Exception("Failed to construct AvrResponse object - invalid response ID."); + if (this->getResponseId() != 0x81) { + throw Exception("Failed to construct AvrResponse object - invalid response ID."); + } + + const auto& responseData = this->getData(); + + if (responseData.empty()) { + // All AVR responses should contain at least one byte (the fragment info byte) + throw Exception("Failed to construct AvrResponse object - AVR_RSP response " + "returned no additional data"); + } + + if (responseData[0] == 0x00) { + // This AVR Response contains no data (the device had no data to send), so we can stop here. + return; + } + + this->setFragmentCount(static_cast(responseData[0] & 0x0FU)); + this->setFragmentNumber(static_cast(responseData[0] >> 4)); + + // Response size is two bytes, MSB + const auto responsePacketSize = static_cast((responseData[1] << 8U) + responseData[2]); + std::vector responsePacket; + responsePacket.resize(responsePacketSize); + + for (std::size_t i = 0; i < responsePacketSize; i++) { + responsePacket[i] = responseData[i + 3]; + } + + this->setResponsePacket(responsePacket); } - - auto& responseData = this->getData(); - - if (responseData.empty()) { - // All AVR responses should contain at least one byte (the fragment info byte) - throw Exception("Failed to construct AvrResponse object - AVR_RSP response " - "returned no additional data"); - } - - if (responseData[0] == 0x00) { - // This AVR Response contains no data (the device had no data to send), so we can stop here. - return; - } - - this->setFragmentCount(static_cast(responseData[0] & 0x0Fu)); - this->setFragmentNumber(static_cast(responseData[0] >> 4)); - - // Response size is two bytes, MSB - std::size_t responsePacketSize = static_cast((responseData[1] << 8u) + responseData[2]); - std::vector responsePacket; - responsePacket.resize(responsePacketSize); - - for (std::size_t i = 0; i < responsePacketSize; i++) { - responsePacket[i] = responseData[i + 3]; - } - - this->setResponsePacket(responsePacket); } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/ReadMemory.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/ReadMemory.cpp index f3192a4c..26f3546b 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/ReadMemory.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/ReadMemory.cpp @@ -3,54 +3,55 @@ #include #include -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames::Avr8Generic; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames::Avr8Generic +{ + std::vector ReadMemory::getPayload() const { + /* + * The read memory command consists of 11/11 + (this->bytes / 8) bytes: + * 1. Command ID (0x21 for the general read memory command, 0x22 for reading with a mask) + * 2. Version (0x00) + * 3. Memory type (Avr8MemoryType) + * 4. Start address (4 bytes) + * 5. Number of bytes to read (4 bytes) + * 6. Mask to apply (this->bytes / 8) - only required if we're using the masked read command (command ID 0x22). + */ + auto output = std::vector(11, 0x00); + output[0] = this->excludedAddresses.empty() ? 0x21 : 0x22; + output[1] = 0x00; + output[2] = static_cast(this->type); + output[3] = static_cast(this->address); + output[4] = static_cast(this->address >> 8); + output[5] = static_cast(this->address >> 16); + output[6] = static_cast(this->address >> 24); + output[7] = static_cast(this->bytes); + output[8] = static_cast(this->bytes >> 8); + output[9] = static_cast(this->bytes >> 16); + output[10] = static_cast(this->bytes >> 24); -std::vector ReadMemory::getPayload() const { - /* - * The read memory command consists of 11/11 + (this->bytes / 8) bytes: - * 1. Command ID (0x21 for the general read memory command, 0x22 for reading with a mask) - * 2. Version (0x00) - * 3. Memory type (Avr8MemoryType) - * 4. Start address (4 bytes) - * 5. Number of bytes to read (4 bytes) - * 6. Mask to apply (this->bytes / 8) - only required if we're using the masked read command (command ID 0x22). - */ - auto output = std::vector(11, 0x00); - output[0] = this->excludedAddresses.empty() ? 0x21 : 0x22; - output[1] = 0x00; - output[2] = static_cast(this->type); - output[3] = static_cast(this->address); - output[4] = static_cast(this->address >> 8); - output[5] = static_cast(this->address >> 16); - output[6] = static_cast(this->address >> 24); - output[7] = static_cast(this->bytes); - output[8] = static_cast(this->bytes >> 8); - output[9] = static_cast(this->bytes >> 16); - output[10] = static_cast(this->bytes >> 24); + if (!this->excludedAddresses.empty()) { + const auto endAddress = this->address + (this->bytes - 1); - if (!this->excludedAddresses.empty()) { - const auto endAddress = this->address + (this->bytes - 1); + constexpr auto byteBitSize = std::numeric_limits::digits; + auto byteBitset = std::bitset(); + byteBitset.reset(); - constexpr auto byteBitSize = std::numeric_limits::digits; - auto byteBitset = std::bitset(); - byteBitset.reset(); + for (std::uint32_t address = this->address; address <= endAddress; address++) { + auto addressIndex = address - this->address; + auto bitIndex = static_cast( + addressIndex - (std::floor(addressIndex / byteBitSize) * byteBitSize) + ); - for (std::uint32_t address = this->address; address <= endAddress; address++) { - auto addressIndex = address - this->address; - auto bitIndex = static_cast( - addressIndex - (std::floor(addressIndex / byteBitSize) * byteBitSize) - ); + if (!this->excludedAddresses.contains(address)) { + byteBitset[bitIndex] = 1; + } - if (!this->excludedAddresses.contains(address)) { - byteBitset[bitIndex] = 1; - } - - if (address > 0 && (bitIndex == 7 || address == endAddress)) { - output.emplace_back(byteBitset.to_ulong()); - byteBitset.reset(); + if (address > 0 && (bitIndex == 7 || address == endAddress)) { + output.emplace_back(byteBitset.to_ulong()); + byteBitset.reset(); + } } } - } - return output; + return output; + } } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AvrCommandFrame.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AvrCommandFrame.cpp index 3a743494..c937c934 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AvrCommandFrame.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AvrCommandFrame.cpp @@ -2,62 +2,63 @@ #include -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; - -std::vector AvrCommandFrame::generateAvrCommands(std::size_t maximumCommandPacketSize) const { - auto rawCommandFrame = static_cast>(*this); - std::size_t commandFrameSize = rawCommandFrame.size(); - auto commandsRequired = static_cast( - ceil(static_cast(commandFrameSize) / static_cast(maximumCommandPacketSize)) - ); - - std::vector avrCommands; - std::size_t copiedPacketSize = 0; - for (std::size_t i = 0; i < commandsRequired; i++) { - AvrCommand avrCommand; - avrCommand.setFragmentCount(commandsRequired); - avrCommand.setFragmentNumber(i + 1); - auto commandPacket = avrCommand.getCommandPacket(); - - // If we're on the last packet, the packet size will be what ever is left of the AvrCommandFrame - std::size_t commandPacketSize = ((i + 1) != commandsRequired) ? maximumCommandPacketSize - : (commandFrameSize - (maximumCommandPacketSize * i)); - - commandPacket.insert( - commandPacket.end(), - rawCommandFrame.begin() + static_cast(copiedPacketSize), - rawCommandFrame.begin() + static_cast(copiedPacketSize + commandPacketSize) +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr +{ + std::vector AvrCommandFrame::generateAvrCommands(std::size_t maximumCommandPacketSize) const { + auto rawCommandFrame = static_cast>(*this); + std::size_t commandFrameSize = rawCommandFrame.size(); + auto commandsRequired = static_cast( + ceil(static_cast(commandFrameSize) / static_cast(maximumCommandPacketSize)) ); - avrCommand.setCommandPacket(commandPacket); - avrCommands.push_back(avrCommand); - copiedPacketSize += commandPacketSize; + std::vector avrCommands; + std::size_t copiedPacketSize = 0; + for (std::size_t i = 0; i < commandsRequired; i++) { + AvrCommand avrCommand; + avrCommand.setFragmentCount(commandsRequired); + avrCommand.setFragmentNumber(i + 1); + auto commandPacket = avrCommand.getCommandPacket(); + + // If we're on the last packet, the packet size will be what ever is left of the AvrCommandFrame + std::size_t commandPacketSize = ((i + 1) != commandsRequired) ? maximumCommandPacketSize + : (commandFrameSize - (maximumCommandPacketSize * i)); + + commandPacket.insert( + commandPacket.end(), + rawCommandFrame.begin() + static_cast(copiedPacketSize), + rawCommandFrame.begin() + static_cast(copiedPacketSize + commandPacketSize) + ); + + avrCommand.setCommandPacket(commandPacket); + avrCommands.push_back(avrCommand); + copiedPacketSize += commandPacketSize; + } + + return avrCommands; } - return avrCommands; -} + AvrCommandFrame::operator std::vector() const { + auto data = this->getPayload(); + auto dataSize = data.size(); -AvrCommandFrame::operator std::vector () const { - auto data = this->getPayload(); - auto dataSize = data.size(); + auto rawCommand = std::vector(5); - auto rawCommand = std::vector(5); + rawCommand[0] = this->SOF; + rawCommand[1] = this->getProtocolVersion(); - rawCommand[0] = this->SOF; - rawCommand[1] = this->getProtocolVersion(); + rawCommand[2] = static_cast(this->getSequenceId()); + rawCommand[3] = static_cast(this->getSequenceId() >> 8); - rawCommand[2] = static_cast(this->getSequenceId()); - rawCommand[3] = static_cast(this->getSequenceId() >> 8); + rawCommand[4] = static_cast(this->getProtocolHandlerId()); - rawCommand[4] = static_cast(this->getProtocolHandlerId()); + if (dataSize > 0) { + rawCommand.insert( + rawCommand.end(), + data.begin(), + data.end() + ); + } - if (dataSize > 0) { - rawCommand.insert( - rawCommand.end(), - data.begin(), - data.end() - ); + return rawCommand; } - - return rawCommand; } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.cpp index ae4c2360..34994b82 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.cpp @@ -36,1399 +36,1373 @@ // AVR events #include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/Events/AVR8Generic/BreakEvent.hpp" -using namespace Bloom::Targets::Microchip::Avr; -using namespace Bloom::Targets::Microchip::Avr::Avr8Bit; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr +{ + using namespace Bloom::Targets::Microchip::Avr; + using namespace Avr8Bit; + using namespace Bloom::Exceptions; -using Bloom::Targets::TargetState; -using Bloom::Targets::TargetMemoryType; -using Bloom::Targets::TargetMemoryBuffer; -using Bloom::Targets::TargetRegister; -using Bloom::Targets::TargetRegisterDescriptor; -using Bloom::Targets::TargetRegisterDescriptors; -using Bloom::Targets::TargetRegisterType; -using Bloom::Targets::TargetRegisters; + using Bloom::Targets::TargetState; + using Bloom::Targets::TargetMemoryType; + using Bloom::Targets::TargetMemoryBuffer; + using Bloom::Targets::TargetRegister; + using Bloom::Targets::TargetRegisterDescriptor; + using Bloom::Targets::TargetRegisterDescriptors; + using Bloom::Targets::TargetRegisterType; + using Bloom::Targets::TargetRegisters; -void EdbgAvr8Interface::configure(const TargetConfig& targetConfig) { - auto physicalInterface = targetConfig.jsonObject.find("physicalInterface")->toString().toLower().toStdString(); + void EdbgAvr8Interface::configure(const TargetConfig& targetConfig) { + auto physicalInterface = targetConfig.jsonObject.find("physicalInterface")->toString().toLower().toStdString(); - auto availablePhysicalInterfaces = this->getPhysicalInterfacesByName(); + auto availablePhysicalInterfaces = this->getPhysicalInterfacesByName(); - if (physicalInterface.empty() - || availablePhysicalInterfaces.find(physicalInterface) == availablePhysicalInterfaces.end() - ) { - throw InvalidConfig("Invalid or missing physical interface config parameter for AVR8 target."); - } - - auto selectedPhysicalInterface = availablePhysicalInterfaces.find(physicalInterface)->second; - - if (selectedPhysicalInterface == PhysicalInterface::DEBUG_WIRE) { - Logger::warning("AVR8 debugWire interface selected - the DWEN fuse will need to be enabled"); - } - - this->physicalInterface = selectedPhysicalInterface; - - if (!this->family.has_value()) { - if (this->physicalInterface == PhysicalInterface::JTAG) { - throw InvalidConfig("The JTAG physical interface cannot be used with an ambiguous target name" - " - please specify the exact name of the target in your configuration file. " - "See " + Paths::homeDomainName() + "/docs/supported-targets" - ); - - } else if (this->physicalInterface == PhysicalInterface::UPDI) { - throw InvalidConfig("The UPDI physical interface cannot be used with an ambiguous target name" - " - please specify the exact name of the target in your configuration file. " - "See " + Paths::homeDomainName() + "/docs/supported-targets" - ); - } - } - - this->configVariant = this->resolveConfigVariant().value_or(Avr8ConfigVariant::NONE); - - if (targetConfig.jsonObject.contains("disableDebugWirePreDisconnect")) { - this->disableDebugWireOnDeactivate = targetConfig.jsonObject.find( - "disableDebugWirePreDisconnect" - )->toBool(); - } -} - -void EdbgAvr8Interface::setTargetParameters(const Avr8Bit::TargetParameters& config) { - this->targetParameters = config; - - if (!config.stackPointerRegisterLowAddress.has_value()) { - throw DeviceInitializationFailure("Failed to find stack pointer register start address"); - } - - if (!config.stackPointerRegisterSize.has_value()) { - throw DeviceInitializationFailure("Failed to find stack pointer register size"); - } - - if (!config.statusRegisterStartAddress.has_value()) { - throw DeviceInitializationFailure("Failed to find status register start address"); - } - - if (!config.statusRegisterSize.has_value()) { - throw DeviceInitializationFailure("Failed to find status register size"); - } - - if (config.flashPageSize.has_value() && this->maximumMemoryAccessSizePerRequest.has_value() - && config.flashPageSize > this->maximumMemoryAccessSizePerRequest - ) { - throw DeviceInitializationFailure("Flash page size for target (" - + std::to_string(config.flashPageSize.value()) - + " bytes) exceeds maximum memory access size for EdbgAvr8Interface (" - + std::to_string(this->maximumMemoryAccessSizePerRequest.value()) + " bytes)." - ); - } - - if (this->configVariant == Avr8ConfigVariant::NONE) { - auto configVariant = this->resolveConfigVariant(); - - if (!configVariant.has_value()) { - throw DeviceInitializationFailure("Failed to resolve config variant for the selected " - "physical interface and AVR8 family. The selected physical interface is not known to be supported " - "by the AVR8 family." - ); + if (physicalInterface.empty() + || availablePhysicalInterfaces.find(physicalInterface) == availablePhysicalInterfaces.end() + ) { + throw InvalidConfig("Invalid or missing physical interface config parameter for AVR8 target."); } - this->configVariant = configVariant.value(); - } + auto selectedPhysicalInterface = availablePhysicalInterfaces.find(physicalInterface)->second; - switch (this->configVariant) { - case Avr8ConfigVariant::DEBUG_WIRE: - case Avr8ConfigVariant::MEGAJTAG: { - this->setDebugWireAndJtagParameters(); - break; + if (selectedPhysicalInterface == PhysicalInterface::DEBUG_WIRE) { + Logger::warning("AVR8 debugWire interface selected - the DWEN fuse will need to be enabled"); } - case Avr8ConfigVariant::XMEGA: { - this->setPdiParameters(); - break; - } - case Avr8ConfigVariant::UPDI: { - this->setUpdiParameters(); - break; - } - default: { - break; - } - } -} -void EdbgAvr8Interface::init() { - if (this->configVariant == Avr8ConfigVariant::XMEGA) { - // Default PDI clock to 4MHz - // TODO: Make this adjustable via a target config parameter - this->setParameter(Avr8EdbgParameters::PDI_CLOCK_SPEED, static_cast(0x0FA0)); - } + this->physicalInterface = selectedPhysicalInterface; - if (this->configVariant == Avr8ConfigVariant::UPDI) { - // Default UPDI clock to 1.8MHz - this->setParameter(Avr8EdbgParameters::PDI_CLOCK_SPEED, static_cast(0x0708)); - this->setParameter(Avr8EdbgParameters::ENABLE_HIGH_VOLTAGE_UPDI, static_cast(0x00)); - } + if (!this->family.has_value()) { + if (this->physicalInterface == PhysicalInterface::JTAG) { + throw InvalidConfig("The JTAG physical interface cannot be used with an ambiguous target name" + " - please specify the exact name of the target in your configuration file. " + "See " + Paths::homeDomainName() + "/docs/supported-targets" + ); - if (this->configVariant == Avr8ConfigVariant::MEGAJTAG) { - // Default clock value for mega debugging is 2KHz - // TODO: Make this adjustable via a target config parameter - this->setParameter(Avr8EdbgParameters::MEGA_DEBUG_CLOCK, static_cast(0x00C8)); - this->setParameter(Avr8EdbgParameters::JTAG_DAISY_CHAIN_SETTINGS, static_cast(0)); - } - - this->setParameter( - Avr8EdbgParameters::CONFIG_VARIANT, - static_cast(this->configVariant) - ); - - this->setParameter( - Avr8EdbgParameters::CONFIG_FUNCTION, - static_cast(this->configFunction) - ); - - this->setParameter( - Avr8EdbgParameters::PHYSICAL_INTERFACE, - getAvr8PhysicalInterfaceToIdMapping().at(this->physicalInterface) - ); -} - -void EdbgAvr8Interface::stop() { - auto commandFrame = CommandFrames::Avr8Generic::Stop(); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Stop target command failed", response); - } - - if (this->getTargetState() == TargetState::RUNNING) { - this->waitForStoppedEvent(); - } -} - -void EdbgAvr8Interface::run() { - this->clearEvents(); - auto commandFrame = CommandFrames::Avr8Generic::Run(); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Run command failed", response); - } - - this->targetState = TargetState::RUNNING; -} - -void EdbgAvr8Interface::runTo(std::uint32_t address) { - this->clearEvents(); - Logger::debug("Running to address: " + std::to_string(address)); - auto commandFrame = CommandFrames::Avr8Generic::RunTo(address); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Run-to command failed", response); - } - - this->targetState = TargetState::RUNNING; -} - -void EdbgAvr8Interface::step() { - auto commandFrame = CommandFrames::Avr8Generic::Step(); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Step target command failed", response); - } - - this->targetState = TargetState::RUNNING; -} - -void EdbgAvr8Interface::reset() { - auto commandFrame = CommandFrames::Avr8Generic::Reset(); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Reset target command failed", response); - } -} - -void EdbgAvr8Interface::activate() { - if (!this->physicalInterfaceActivated) { - this->activatePhysical(); - } - - if (!this->targetAttached) { - this->attach(); - } -} - -void EdbgAvr8Interface::deactivate() { - if (this->targetAttached) { - if (this->physicalInterface == PhysicalInterface::DEBUG_WIRE && this->disableDebugWireOnDeactivate) { - try { - this->disableDebugWire(); - Logger::warning("Successfully disabled debugWire on the AVR8 target - this is only temporary - " - "the debugWire module has lost control of the RESET pin. Bloom will no longer be able to interface " - "with the target until the next power cycle."); - - } catch (const Exception& exception) { - // Failing to disable debugWire should never prevent us from proceeding with target deactivation. - Logger::error(exception.getMessage()); + } else if (this->physicalInterface == PhysicalInterface::UPDI) { + throw InvalidConfig("The UPDI physical interface cannot be used with an ambiguous target name" + " - please specify the exact name of the target in your configuration file. " + "See " + Paths::homeDomainName() + "/docs/supported-targets" + ); } } - this->stop(); - this->clearAllBreakpoints(); - this->run(); + this->configVariant = this->resolveConfigVariant().value_or(Avr8ConfigVariant::NONE); - this->detach(); + if (targetConfig.jsonObject.contains("disableDebugWirePreDisconnect")) { + this->disableDebugWireOnDeactivate = targetConfig.jsonObject.find( + "disableDebugWirePreDisconnect" + )->toBool(); + } } - if (this->physicalInterfaceActivated) { - this->deactivatePhysical(); - } -} + void EdbgAvr8Interface::setTargetParameters(const Avr8Bit::TargetParameters& config) { + this->targetParameters = config; -std::uint32_t EdbgAvr8Interface::getProgramCounter() { - if (this->targetState != TargetState::STOPPED) { - this->stop(); + if (!config.stackPointerRegisterLowAddress.has_value()) { + throw DeviceInitializationFailure("Failed to find stack pointer register start address"); + } + + if (!config.stackPointerRegisterSize.has_value()) { + throw DeviceInitializationFailure("Failed to find stack pointer register size"); + } + + if (!config.statusRegisterStartAddress.has_value()) { + throw DeviceInitializationFailure("Failed to find status register start address"); + } + + if (!config.statusRegisterSize.has_value()) { + throw DeviceInitializationFailure("Failed to find status register size"); + } + + if (config.flashPageSize.has_value() && this->maximumMemoryAccessSizePerRequest.has_value() + && config.flashPageSize > this->maximumMemoryAccessSizePerRequest + ) { + throw DeviceInitializationFailure("Flash page size for target (" + + std::to_string(config.flashPageSize.value()) + + " bytes) exceeds maximum memory access size for EdbgAvr8Interface (" + + std::to_string(this->maximumMemoryAccessSizePerRequest.value()) + + " bytes)." + ); + } + + if (this->configVariant == Avr8ConfigVariant::NONE) { + auto configVariant = this->resolveConfigVariant(); + + if (!configVariant.has_value()) { + throw DeviceInitializationFailure("Failed to resolve config variant for the selected " + "physical interface and AVR8 family. The selected physical interface is not known to be supported " + "by the AVR8 family." + ); + } + + this->configVariant = configVariant.value(); + } + + switch (this->configVariant) { + case Avr8ConfigVariant::DEBUG_WIRE: + case Avr8ConfigVariant::MEGAJTAG: { + this->setDebugWireAndJtagParameters(); + break; + } + case Avr8ConfigVariant::XMEGA: { + this->setPdiParameters(); + break; + } + case Avr8ConfigVariant::UPDI: { + this->setUpdiParameters(); + break; + } + default: { + break; + } + } } - auto commandFrame = CommandFrames::Avr8Generic::GetProgramCounter(); - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Get program counter command failed", response); + void EdbgAvr8Interface::init() { + if (this->configVariant == Avr8ConfigVariant::XMEGA) { + // Default PDI clock to 4MHz + // TODO: Make this adjustable via a target config parameter + this->setParameter(Avr8EdbgParameters::PDI_CLOCK_SPEED, static_cast(0x0FA0)); + } + + if (this->configVariant == Avr8ConfigVariant::UPDI) { + // Default UPDI clock to 1.8MHz + this->setParameter(Avr8EdbgParameters::PDI_CLOCK_SPEED, static_cast(0x0708)); + this->setParameter(Avr8EdbgParameters::ENABLE_HIGH_VOLTAGE_UPDI, static_cast(0x00)); + } + + if (this->configVariant == Avr8ConfigVariant::MEGAJTAG) { + // Default clock value for mega debugging is 2KHz + // TODO: Make this adjustable via a target config parameter + this->setParameter(Avr8EdbgParameters::MEGA_DEBUG_CLOCK, static_cast(0x00C8)); + this->setParameter(Avr8EdbgParameters::JTAG_DAISY_CHAIN_SETTINGS, static_cast(0)); + } + + this->setParameter( + Avr8EdbgParameters::CONFIG_VARIANT, + static_cast(this->configVariant) + ); + + this->setParameter( + Avr8EdbgParameters::CONFIG_FUNCTION, + static_cast(this->configFunction) + ); + + this->setParameter( + Avr8EdbgParameters::PHYSICAL_INTERFACE, + getAvr8PhysicalInterfaceToIdMapping().at(this->physicalInterface) + ); } - return response.extractProgramCounter(); -} + void EdbgAvr8Interface::stop() { + auto commandFrame = CommandFrames::Avr8Generic::Stop(); -void EdbgAvr8Interface::setProgramCounter(std::uint32_t programCounter) { - if (this->targetState != TargetState::STOPPED) { - this->stop(); + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Stop target command failed", response); + } + + if (this->getTargetState() == TargetState::RUNNING) { + this->waitForStoppedEvent(); + } } - /* - * The program counter will be given in byte address form, but the EDBG tool will be expecting it in word - * address (16-bit) form. This is why we divide it by 2. - */ - auto commandFrame = CommandFrames::Avr8Generic::SetProgramCounter(programCounter / 2); - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Set program counter command failed", response); - } -} + void EdbgAvr8Interface::run() { + this->clearEvents(); + auto commandFrame = CommandFrames::Avr8Generic::Run(); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Run command failed", response); + } + + this->targetState = TargetState::RUNNING; + } + + void EdbgAvr8Interface::runTo(std::uint32_t address) { + this->clearEvents(); + auto commandFrame = CommandFrames::Avr8Generic::RunTo(address); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Run-to command failed", response); + } + + this->targetState = TargetState::RUNNING; + } + + void EdbgAvr8Interface::step() { + auto commandFrame = CommandFrames::Avr8Generic::Step(); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Step target command failed", response); + } + + this->targetState = TargetState::RUNNING; + } + + void EdbgAvr8Interface::reset() { + auto commandFrame = CommandFrames::Avr8Generic::Reset(); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Reset target command failed", response); + } + } + + void EdbgAvr8Interface::activate() { + if (!this->physicalInterfaceActivated) { + this->activatePhysical(); + } + + if (!this->targetAttached) { + this->attach(); + } + } + + void EdbgAvr8Interface::deactivate() { + if (this->targetAttached) { + if (this->physicalInterface == PhysicalInterface::DEBUG_WIRE && this->disableDebugWireOnDeactivate) { + try { + this->disableDebugWire(); + Logger::warning( + "Successfully disabled debugWire on the AVR8 target - this is only temporary - " + "the debugWire module has lost control of the RESET pin. Bloom will no longer be able to " + "interface with the target until the next power cycle." + ); + + } catch (const Exception& exception) { + // Failing to disable debugWire should never prevent us from proceeding with target deactivation. + Logger::error(exception.getMessage()); + } + } + + this->stop(); + this->clearAllBreakpoints(); + this->run(); + + this->detach(); + } + + if (this->physicalInterfaceActivated) { + this->deactivatePhysical(); + } + } + + std::uint32_t EdbgAvr8Interface::getProgramCounter() { + if (this->targetState != TargetState::STOPPED) { + this->stop(); + } + + auto commandFrame = CommandFrames::Avr8Generic::GetProgramCounter(); + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Get program counter command failed", response); + } + + return response.extractProgramCounter(); + } + + void EdbgAvr8Interface::setProgramCounter(std::uint32_t programCounter) { + if (this->targetState != TargetState::STOPPED) { + this->stop(); + } -TargetSignature EdbgAvr8Interface::getDeviceId() { - if (this->configVariant == Avr8ConfigVariant::UPDI) { /* - * When using the UPDI physical interface, the 'Get device ID' command behaves in an odd manner, where it - * doesn't actually return the target signature, but instead a fixed four byte string reading: - * 'A', 'V', 'R' and ' ' (white space). - * - * So it appears we cannot use that command for UPDI sessions. As an alternative, we will just read the - * signature from memory using the signature base address. - * - * TODO: Currently, we're assuming the signature will always only ever be three bytes in size, but we may - * want to consider pulling the size from the TDF. + * The program counter will be given in byte address form, but the EDBG tool will be expecting it in word + * address (16-bit) form. This is why we divide it by 2. */ - auto signatureMemory = this->readMemory( - Avr8MemoryType::SRAM, - this->targetParameters.signatureSegmentStartAddress.value(), - 3 - ); - - if (signatureMemory.size() != 3) { - throw Exception("Failed to read AVR8 signature from target - unexpected response size"); + auto commandFrame = CommandFrames::Avr8Generic::SetProgramCounter(programCounter / 2); + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Set program counter command failed", response); } - - return TargetSignature(signatureMemory[0], signatureMemory[1], signatureMemory[2]); } - auto commandFrame = CommandFrames::Avr8Generic::GetDeviceId(); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Get device ID command failed", response); - } - - return response.extractSignature(this->physicalInterface); -} - -void EdbgAvr8Interface::setBreakpoint(std::uint32_t address) { - auto commandFrame = CommandFrames::Avr8Generic::SetSoftwareBreakpoints({address}); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Set software breakpoint command failed", response); - } -} - -void EdbgAvr8Interface::clearBreakpoint(std::uint32_t address) { - auto commandFrame = CommandFrames::Avr8Generic::ClearSoftwareBreakpoints({address}); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Clear software breakpoint command failed", response); - } -} - -void EdbgAvr8Interface::clearAllBreakpoints() { - auto commandFrame = CommandFrames::Avr8Generic::ClearAllSoftwareBreakpoints(); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Clear all software breakpoints command failed", response); - } -} - -TargetRegisters EdbgAvr8Interface::readRegisters(const TargetRegisterDescriptors& descriptors) { - /* - * This function needs to be fast. Insight eagerly requests the values of all known registers that it can present - * to the user. It does this on numerous occasions (target stopped, user clicked refresh, etc). This means we will - * be frequently loading over 100 register values in a single instance. - * - * For the above reason, we do not read each register value individually. That would take far too long if we have - * over 100 registers to read. Instead, we group the register descriptors into collections by register type, and - * resolve the address range for each collection. We then perform a single read operation for each collection - * and hold the memory buffer in a random access container (std::vector). Finally, we extract the data for - * each register descriptor, from the memory buffer, and construct the relevant TargetRegister object. - * - * TODO: We should be grouping the register descriptors by memory type, as opposed to register type. This - * isn't much of a problem ATM, as currently, we only work with registers that are stored in the data - * address space or the register file. This will need to be addressed before we can work with any other - * registers stored elsewhere. - */ - auto output = TargetRegisters(); - - // Group descriptors by type and resolve the address range for each type - auto descriptorsByType = std::map>(); - - /* - * An address range is just an std::pair of integers - the first being the start address, the second being the - * end address. - */ - using AddressRange = std::pair; - auto addressRangeByType = std::map(); - - for (const auto& descriptor : descriptors) { - if (!descriptor.startAddress.has_value()) { - Logger::debug( - "Attempted to read register in the absence of a start address - register name: " - + descriptor.name.value_or("unknown") + TargetSignature EdbgAvr8Interface::getDeviceId() { + if (this->configVariant == Avr8ConfigVariant::UPDI) { + /* + * When using the UPDI physical interface, the 'Get device ID' command behaves in an odd manner, where it + * doesn't actually return the target signature, but instead a fixed four byte string reading: + * 'A', 'V', 'R' and ' ' (white space). + * + * So it appears we cannot use that command for UPDI sessions. As an alternative, we will just read the + * signature from memory using the signature base address. + * + * TODO: Currently, we're assuming the signature will always only ever be three bytes in size, but we may + * want to consider pulling the size from the TDF. + */ + auto signatureMemory = this->readMemory( + Avr8MemoryType::SRAM, + this->targetParameters.signatureSegmentStartAddress.value(), + 3 ); - continue; + + if (signatureMemory.size() != 3) { + throw Exception("Failed to read AVR8 signature from target - unexpected response size"); + } + + return TargetSignature(signatureMemory[0], signatureMemory[1], signatureMemory[2]); } - descriptorsByType[descriptor.type].insert(&descriptor); + auto commandFrame = CommandFrames::Avr8Generic::GetDeviceId(); - const auto startAddress = descriptor.startAddress.value(); - const auto endAddress = startAddress + (descriptor.size - 1); + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Get device ID command failed", response); + } - if (!addressRangeByType.contains(descriptor.type)) { - auto addressRange = AddressRange(); - addressRange.first = startAddress; - addressRange.second = endAddress; - addressRangeByType[descriptor.type] = addressRange; + return response.extractSignature(this->physicalInterface); + } - } else { - auto& addressRange = addressRangeByType[descriptor.type]; + void EdbgAvr8Interface::setBreakpoint(std::uint32_t address) { + auto commandFrame = CommandFrames::Avr8Generic::SetSoftwareBreakpoints({address}); - if (startAddress < addressRange.first) { - addressRange.first = startAddress; - } - - if (endAddress > addressRange.second) { - addressRange.second = endAddress; - } + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Set software breakpoint command failed", response); } } - /* - * Now that we have our address ranges and grouped descriptors, we can perform a single read call for each - * register type. - */ - for (const auto& [registerType, descriptors] : descriptorsByType) { - const auto& addressRange = addressRangeByType[registerType]; - const auto startAddress = addressRange.first; - const auto endAddress = addressRange.second; - const auto bufferSize = (endAddress - startAddress) + 1; + void EdbgAvr8Interface::clearBreakpoint(std::uint32_t address) { + auto commandFrame = CommandFrames::Avr8Generic::ClearSoftwareBreakpoints({address}); - const auto memoryType = (registerType != TargetRegisterType::GENERAL_PURPOSE_REGISTER) ? Avr8MemoryType::SRAM : ( - this->configVariant == Avr8ConfigVariant::XMEGA || this->configVariant == Avr8ConfigVariant::UPDI - ? Avr8MemoryType::REGISTER_FILE : Avr8MemoryType::SRAM - ); + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Clear software breakpoint command failed", response); + } + } + + void EdbgAvr8Interface::clearAllBreakpoints() { + auto commandFrame = CommandFrames::Avr8Generic::ClearAllSoftwareBreakpoints(); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Clear all software breakpoints command failed", response); + } + } + + TargetRegisters EdbgAvr8Interface::readRegisters(const TargetRegisterDescriptors& descriptors) { + /* + * This function needs to be fast. Insight eagerly requests the values of all known registers that it can + * present to the user. It does this on numerous occasions (target stopped, user clicked refresh, etc). This + * means we will be frequently loading over 100 register values in a single instance. + * + * For the above reason, we do not read each register value individually. That would take far too long if we + * have over 100 registers to read. Instead, we group the register descriptors into collections by register + * type, and resolve the address range for each collection. We then perform a single read operation for + * each collection and hold the memory buffer in a random access container (std::vector). Finally, we extract + * the data for each register descriptor, from the memory buffer, and construct the relevant TargetRegister + * object. + * + * TODO: We should be grouping the register descriptors by memory type, as opposed to register type. This + * isn't much of a problem ATM, as currently, we only work with registers that are stored in the data + * address space or the register file. This will need to be addressed before we can work with any other + * registers stored elsewhere. + */ + auto output = TargetRegisters(); + + // Group descriptors by type and resolve the address range for each type + auto descriptorsByType = std::map>(); /* - * When reading the entire range, we must avoid any attempts to access the OCD data register (OCDDR), as the - * debug tool will reject the command and respond with a 0x36 error code (invalid address error). + * An address range is just an std::pair of integers - the first being the start address, the second being the + * end address. + */ + using AddressRange = std::pair; + auto addressRangeByType = std::map(); + + for (const auto& descriptor : descriptors) { + if (!descriptor.startAddress.has_value()) { + Logger::debug( + "Attempted to read register in the absence of a start address - register name: " + + descriptor.name.value_or("unknown") + ); + continue; + } + + descriptorsByType[descriptor.type].insert(&descriptor); + + const auto startAddress = descriptor.startAddress.value(); + const auto endAddress = startAddress + (descriptor.size - 1); + + if (!addressRangeByType.contains(descriptor.type)) { + auto addressRange = AddressRange(); + addressRange.first = startAddress; + addressRange.second = endAddress; + addressRangeByType[descriptor.type] = addressRange; + + } else { + auto& addressRange = addressRangeByType[descriptor.type]; + + if (startAddress < addressRange.first) { + addressRange.first = startAddress; + } + + if (endAddress > addressRange.second) { + addressRange.second = endAddress; + } + } + } + + /* + * Now that we have our address ranges and grouped descriptors, we can perform a single read call for each + * register type. + */ + for (const auto&[registerType, descriptors] : descriptorsByType) { + const auto& addressRange = addressRangeByType[registerType]; + const auto startAddress = addressRange.first; + const auto endAddress = addressRange.second; + const auto bufferSize = (endAddress - startAddress) + 1; + + const auto memoryType = (registerType != TargetRegisterType::GENERAL_PURPOSE_REGISTER) ? + Avr8MemoryType::SRAM + : (this->configVariant == Avr8ConfigVariant::XMEGA || this->configVariant == Avr8ConfigVariant::UPDI + ? Avr8MemoryType::REGISTER_FILE : Avr8MemoryType::SRAM); + + /* + * When reading the entire range, we must avoid any attempts to access the OCD data register (OCDDR), as + * the debug tool will reject the command and respond with a 0x36 error code (invalid address error). + * + * For this reason, we specify the OCDDR address as an excluded address. This will mean + * the EdbgAvr8Interface::readMemory() function will employ the masked read memory command, as opposed to + * the general read memory command. The masked read memory command allows us to specify which addresses to + * read and which ones to ignore. For ignored addresses, the debug tool will just return a 0x00 byte. + * For more info, see section 7.1.22 titled 'Memory Read Masked', in the EDBG protocol document. + * + * Interestingly, the masked read memory command doesn't seem to require us to explicitly specify the OCDDR + * address as an excluded address. It seems to exclude the OCDDR automatically, even if we've not + * instructed it to do so. This is plausible, as we send the OCDDR address to the debug tool during target + * initialisation (see EdbgAvr8Interface::setDebugWireAndJtagParameters()). So this means we don't have to + * specify the OCDDR address as an excluded address, but the EdbgAvr8Interface::readMemory() function will + * only employ the masked read memory command when we supply at least one excluded address. For this reason, + * we still pass the OCDDR address to EdbgAvr8Interface::readMemory(), as an excluded address (provided we + * have it). + * + * See CommandFrames::Avr8Generic::ReadMemory(); and the Microchip EDBG documentation for more. + */ + auto excludedAddresses = std::set(); + if (memoryType == Avr8MemoryType::SRAM && this->targetParameters.ocdDataRegister.has_value()) { + excludedAddresses.insert( + this->targetParameters.ocdDataRegister.value() + + this->targetParameters.mappedIoSegmentStartAddress.value_or(0) + ); + } + + const auto flatMemoryBuffer = this->readMemory( + memoryType, + startAddress, + bufferSize, + excludedAddresses + ); + + if (flatMemoryBuffer.size() != bufferSize) { + throw Exception( + "Failed to read memory within register type address range (" + std::to_string(startAddress) + + " - " + std::to_string(endAddress) + "). Expected " + std::to_string(bufferSize) + + " bytes, got " + std::to_string(flatMemoryBuffer.size()) + ); + } + + // Construct our TargetRegister objects directly from the flat memory buffer + for (const auto& descriptor : descriptors) { + /* + * Multi-byte AVR8 registers are stored in LSB form. + * + * This is why we use reverse iterators when extracting our data from flatMemoryBuffer. Doing so allows + * us to extract the data in MSB form (as is expected for all register values held in TargetRegister + * objects). + */ + const auto bufferStartIt = flatMemoryBuffer.rend() - (descriptor->startAddress.value() - startAddress) + - descriptor->size; + + output.emplace_back( + TargetRegister( + *descriptor, + TargetMemoryBuffer(bufferStartIt, bufferStartIt + descriptor->size) + ) + ); + } + } + + return output; + } + + void EdbgAvr8Interface::writeRegisters(const Targets::TargetRegisters& registers) { + for (const auto& reg : registers) { + const auto& registerDescriptor = reg.descriptor; + auto registerValue = reg.value; + + if (registerValue.empty()) { + throw Exception("Cannot write empty register value"); + } + + if (registerValue.size() > registerDescriptor.size) { + throw Exception("Register value exceeds size specified by register descriptor."); + } + + if (registerValue.size() < registerDescriptor.size) { + // Fill the missing most-significant bytes with 0x00 + registerValue.insert(registerValue.begin(), registerDescriptor.size - registerValue.size(), 0x00); + } + + if (registerValue.size() > 1) { + // AVR8 registers are stored in LSB + std::reverse(registerValue.begin(), registerValue.end()); + } + + auto memoryType = Avr8MemoryType::SRAM; + if (registerDescriptor.type == TargetRegisterType::GENERAL_PURPOSE_REGISTER + && (this->configVariant == Avr8ConfigVariant::XMEGA || this->configVariant == Avr8ConfigVariant::UPDI) + ) { + memoryType = Avr8MemoryType::REGISTER_FILE; + } + + // TODO: This can be inefficient when updating many registers, maybe do something a little smarter here. + this->writeMemory( + memoryType, + registerDescriptor.startAddress.value(), + registerValue + ); + } + } + + TargetMemoryBuffer EdbgAvr8Interface::readMemory( + TargetMemoryType memoryType, + std::uint32_t startAddress, + std::uint32_t bytes, + const std::set& excludedAddressRanges + ) { + auto avr8MemoryType = Avr8MemoryType::SRAM; + + switch (memoryType) { + case TargetMemoryType::RAM: { + avr8MemoryType = Avr8MemoryType::SRAM; + break; + } + case TargetMemoryType::FLASH: { + if (this->configVariant == Avr8ConfigVariant::DEBUG_WIRE) { + avr8MemoryType = Avr8MemoryType::FLASH_PAGE; + + } else if (this->configVariant == Avr8ConfigVariant::XMEGA + || this->configVariant == Avr8ConfigVariant::UPDI) { + avr8MemoryType = Avr8MemoryType::APPL_FLASH; + + } else { + avr8MemoryType = Avr8MemoryType::SPM; + } + break; + } + case TargetMemoryType::EEPROM: { + avr8MemoryType = Avr8MemoryType::EEPROM; + } + default: { + break; + } + } + + /* + * The internal readMemory() function accepts excluded addresses in the form of a set of addresses, as + * opposed to a set of address ranges. * - * For this reason, we specify the OCDDR address as an excluded address. This will mean - * the EdbgAvr8Interface::readMemory() function will employ the masked read memory command, as opposed to the - * general read memory command. The masked read memory command allows us to specify which addresses to - * read and which ones to ignore. For ignored addresses, the debug tool will just return a 0x00 byte. - * For more info, see section 7.1.22 titled 'Memory Read Masked', in the EDBG protocol document. - * - * Interestingly, the masked read memory command doesn't seem to require us to explicitly specify the OCDDR - * address as an excluded address. It seems to exclude the OCDDR automatically, even if we've not - * instructed it to do so. This is plausible, as we send the OCDDR address to the debug tool during target - * initialisation (see EdbgAvr8Interface::setDebugWireAndJtagParameters()). So this means we don't have to - * specify the OCDDR address as an excluded address, but the EdbgAvr8Interface::readMemory() function will - * only employ the masked read memory command when we supply at least one excluded address. For this reason, - * we still pass the OCDDR address to EdbgAvr8Interface::readMemory(), as an excluded address (provided we - * have it). - * - * See CommandFrames::Avr8Generic::ReadMemory(); and the Microchip EDBG documentation for more. + * We will perform the conversion here. */ auto excludedAddresses = std::set(); - if (memoryType == Avr8MemoryType::SRAM && this->targetParameters.ocdDataRegister.has_value()) { - excludedAddresses.insert( + auto endAddress = startAddress + bytes - 1; + + for (const auto& addressRange : excludedAddressRanges) { + if (addressRange.startAddress > endAddress) { + // This address range is outside of the range from which we will be reading + continue; + } + + for (auto i = addressRange.startAddress; i <= addressRange.endAddress; i++) { + excludedAddresses.insert(i); + } + } + + return this->readMemory(avr8MemoryType, startAddress, bytes, excludedAddresses); + } + + void EdbgAvr8Interface::writeMemory( + TargetMemoryType memoryType, + std::uint32_t startAddress, + const TargetMemoryBuffer& buffer + ) { + auto avr8MemoryType = Avr8MemoryType::SRAM; + + switch (memoryType) { + case TargetMemoryType::RAM: { + avr8MemoryType = Avr8MemoryType::SRAM; + break; + } + case TargetMemoryType::FLASH: { + if (this->configVariant == Avr8ConfigVariant::DEBUG_WIRE) { + avr8MemoryType = Avr8MemoryType::FLASH_PAGE; + + } else if (this->configVariant == Avr8ConfigVariant::MEGAJTAG) { + avr8MemoryType = Avr8MemoryType::FLASH_PAGE; + // TODO: Enable programming mode + + } else if (this->configVariant == Avr8ConfigVariant::XMEGA + || this->configVariant == Avr8ConfigVariant::UPDI) { + avr8MemoryType = Avr8MemoryType::APPL_FLASH; + + } else { + avr8MemoryType = Avr8MemoryType::SPM; + } + break; + } + case TargetMemoryType::EEPROM: { + avr8MemoryType = Avr8MemoryType::EEPROM; + } + default: { + break; + } + } + + return this->writeMemory(avr8MemoryType, startAddress, buffer); + } + + TargetState EdbgAvr8Interface::getTargetState() { + /* + * We are not informed when a target goes from a stopped state to a running state, so there is no need + * to query the tool when we already know the target has stopped. + * + * This means we have to rely on the assumption that the target cannot enter a running state without + * our instruction. + */ + if (this->targetState != TargetState::STOPPED) { + this->refreshTargetState(); + } + + return this->targetState; + } + + void EdbgAvr8Interface::setParameter(const Avr8EdbgParameter& parameter, const std::vector& value) { + auto commandFrame = CommandFrames::Avr8Generic::SetParameter(parameter, value); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("Failed to set parameter on device!", response); + } + } + + std::vector EdbgAvr8Interface::getParameter(const Avr8EdbgParameter& parameter, std::uint8_t size) { + auto commandFrame = CommandFrames::Avr8Generic::GetParameter(parameter, size); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("Failed to get parameter from device!", response); + } + + return response.getPayloadData(); + } + + void EdbgAvr8Interface::setDebugWireAndJtagParameters() { + if (this->targetParameters.flashPageSize.has_value()) { + Logger::debug("Setting DEVICE_FLASH_PAGE_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_FLASH_PAGE_SIZE, + this->targetParameters.flashPageSize.value() + ); + } + + if (this->targetParameters.flashSize.has_value()) { + Logger::debug("Setting DEVICE_FLASH_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_FLASH_SIZE, + this->targetParameters.flashSize.value() + ); + } + + if (this->targetParameters.flashStartAddress.has_value()) { + Logger::debug("Setting DEVICE_FLASH_BASE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_FLASH_BASE, + this->targetParameters.flashStartAddress.value() + ); + } + + if (this->targetParameters.ramStartAddress.has_value()) { + Logger::debug("Setting DEVICE_SRAM_START AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_SRAM_START, + this->targetParameters.ramStartAddress.value() + ); + } + + if (this->targetParameters.eepromSize.has_value()) { + Logger::debug("Setting DEVICE_EEPROM_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_EEPROM_SIZE, + this->targetParameters.eepromSize.value() + ); + } + + if (this->targetParameters.eepromPageSize.has_value()) { + Logger::debug("Setting DEVICE_EEPROM_PAGE_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_EEPROM_PAGE_SIZE, + this->targetParameters.eepromPageSize.value() + ); + } + + if (this->targetParameters.ocdRevision.has_value()) { + Logger::debug("Setting DEVICE_OCD_REVISION AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_OCD_REVISION, + this->targetParameters.ocdRevision.value() + ); + } + + if (this->targetParameters.ocdDataRegister.has_value()) { + Logger::debug("Setting DEVICE_OCD_DATA_REGISTER AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_OCD_DATA_REGISTER, this->targetParameters.ocdDataRegister.value() - + this->targetParameters.mappedIoSegmentStartAddress.value_or(0) ); } - const auto flatMemoryBuffer = this->readMemory( - memoryType, - startAddress, - bufferSize, - excludedAddresses - ); - - if (flatMemoryBuffer.size() != bufferSize) { - throw Exception( - "Failed to read memory within register type address range (" + std::to_string(startAddress) - + " - " + std::to_string(endAddress) +"). Expected " + std::to_string(bufferSize) - + " bytes, got " + std::to_string(flatMemoryBuffer.size()) + if (this->targetParameters.spmcRegisterStartAddress.has_value()) { + Logger::debug("Setting DEVICE_SPMCR_REGISTER AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_SPMCR_REGISTER, + this->targetParameters.spmcRegisterStartAddress.value() ); } - // Construct our TargetRegister objects directly from the flat memory buffer - for (const auto& descriptor : descriptors) { - /* - * Multi-byte AVR8 registers are stored in LSB form. - * - * This is why we use reverse iterators when extracting our data from flatMemoryBuffer. Doing so allows - * us to extract the data in MSB form (as is expected for all register values held in TargetRegister - * objects). - */ - const auto bufferStartIt = flatMemoryBuffer.rend() - (descriptor->startAddress.value() - startAddress) - - descriptor->size; - - output.emplace_back( - TargetRegister( - *descriptor, - TargetMemoryBuffer(bufferStartIt, bufferStartIt + descriptor->size) - ) + if (this->targetParameters.bootSectionStartAddress.has_value()) { + Logger::debug("Setting DEVICE_BOOT_START_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_BOOT_START_ADDR, + this->targetParameters.bootSectionStartAddress.value() ); } - } - return output; -} + /* + * All addresses for registers that reside in the mapped IO memory segment include the mapped IO segment offset + * (start address). But the EDBG protocol requires *some* of these addresses to be stripped of this offset + * before sending them as target parameters. + * + * This applies to the following addresses: + * + * - OSCALL Address + * - EEARL Address + * - EEARH Address + * - EECR Address + * - EEDR Address + * + * It *doesn't* seem to apply to the SPMCR or OCDDR address. + */ + auto mappedIoStartAddress = this->targetParameters.mappedIoSegmentStartAddress.value_or(0); -void EdbgAvr8Interface::writeRegisters(const Targets::TargetRegisters& registers) { - for (const auto& reg : registers) { - const auto& registerDescriptor = reg.descriptor; - auto registerValue = reg.value; - - if (registerValue.empty()) { - throw Exception("Cannot write empty register value"); - } - - if (registerValue.size() > registerDescriptor.size) { - throw Exception("Register value exceeds size specified by register descriptor."); - } - - if (registerValue.size() < registerDescriptor.size) { - // Fill the missing most-significant bytes with 0x00 - registerValue.insert(registerValue.begin(), registerDescriptor.size - registerValue.size(), 0x00); - } - - if (registerValue.size() > 1) { - // AVR8 registers are stored in LSB - std::reverse(registerValue.begin(), registerValue.end()); - } - - auto memoryType = Avr8MemoryType::SRAM; - if (registerDescriptor.type == TargetRegisterType::GENERAL_PURPOSE_REGISTER - && (this->configVariant == Avr8ConfigVariant::XMEGA || this->configVariant == Avr8ConfigVariant::UPDI) - ) { - memoryType = Avr8MemoryType::REGISTER_FILE; - } - - // TODO: This can be inefficient when updating many registers, maybe do something a little smarter here. - this->writeMemory( - memoryType, - registerDescriptor.startAddress.value(), - registerValue - ); - } -} - -TargetMemoryBuffer EdbgAvr8Interface::readMemory( - TargetMemoryType memoryType, - std::uint32_t startAddress, - std::uint32_t bytes, - const std::set& excludedAddressRanges -) { - auto avr8MemoryType = Avr8MemoryType::SRAM; - - switch (memoryType) { - case TargetMemoryType::RAM: { - avr8MemoryType = Avr8MemoryType::SRAM; - break; - } - case TargetMemoryType::FLASH: { - if (this->configVariant == Avr8ConfigVariant::DEBUG_WIRE) { - avr8MemoryType = Avr8MemoryType::FLASH_PAGE; - - } else if (this->configVariant == Avr8ConfigVariant::XMEGA || this->configVariant == Avr8ConfigVariant::UPDI) { - avr8MemoryType = Avr8MemoryType::APPL_FLASH; - - } else { - avr8MemoryType = Avr8MemoryType::SPM; - } - break; - } - case TargetMemoryType::EEPROM: { - avr8MemoryType = Avr8MemoryType::EEPROM; - } - default: { - break; - } - } - - /* - * The internal readMemory() function accepts excluded addresses in the form of a set of addresses, as - * opposed to a set of address ranges. - * - * We will perform the conversion here. - */ - auto excludedAddresses = std::set(); - auto endAddress = startAddress + bytes - 1; - - for (const auto& addressRange : excludedAddressRanges) { - if (addressRange.startAddress > endAddress) { - // This address range is outside of the range from which we will be reading - continue; - } - - for (auto i = addressRange.startAddress; i <= addressRange.endAddress; i++) { - excludedAddresses.insert(i); - } - } - - return this->readMemory(avr8MemoryType, startAddress, bytes, excludedAddresses); -} - -void EdbgAvr8Interface::writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) { - auto avr8MemoryType = Avr8MemoryType::SRAM; - - switch (memoryType) { - case TargetMemoryType::RAM: { - avr8MemoryType = Avr8MemoryType::SRAM; - break; - } - case TargetMemoryType::FLASH: { - if (this->configVariant == Avr8ConfigVariant::DEBUG_WIRE) { - avr8MemoryType = Avr8MemoryType::FLASH_PAGE; - - } else if (this->configVariant == Avr8ConfigVariant::MEGAJTAG) { - avr8MemoryType = Avr8MemoryType::FLASH_PAGE; - // TODO: Enable programming mode - - } else if (this->configVariant == Avr8ConfigVariant::XMEGA || this->configVariant == Avr8ConfigVariant::UPDI) { - avr8MemoryType = Avr8MemoryType::APPL_FLASH; - - } else { - avr8MemoryType = Avr8MemoryType::SPM; - } - break; - } - case TargetMemoryType::EEPROM: { - avr8MemoryType = Avr8MemoryType::EEPROM; - } - default: { - break; - } - } - - return this->writeMemory(avr8MemoryType, startAddress, buffer); -} - -TargetState EdbgAvr8Interface::getTargetState() { - /* - * We are not informed when a target goes from a stopped state to a running state, so there is no need - * to query the tool when we already know the target has stopped. - * - * This means we have to rely on the assumption that the target cannot enter a running state without - * our instruction. - */ - if (this->targetState != TargetState::STOPPED) { - this->refreshTargetState(); - } - - return this->targetState; -} - -void EdbgAvr8Interface::setParameter(const Avr8EdbgParameter& parameter, const std::vector& value) { - auto commandFrame = CommandFrames::Avr8Generic::SetParameter(parameter, value); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("Failed to set parameter on device!", response); - } -} - -std::vector EdbgAvr8Interface::getParameter(const Avr8EdbgParameter& parameter, std::uint8_t size) { - auto commandFrame = CommandFrames::Avr8Generic::GetParameter(parameter, size); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("Failed to get parameter from device!", response); - } - - return response.getPayloadData(); -} - -void EdbgAvr8Interface::setDebugWireAndJtagParameters() { - if (this->targetParameters.flashPageSize.has_value()) { - Logger::debug("Setting DEVICE_FLASH_PAGE_SIZE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_FLASH_PAGE_SIZE, - this->targetParameters.flashPageSize.value() - ); - } - - if (this->targetParameters.flashSize.has_value()) { - Logger::debug("Setting DEVICE_FLASH_SIZE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_FLASH_SIZE, - this->targetParameters.flashSize.value() - ); - } - - if (this->targetParameters.flashStartAddress.has_value()) { - Logger::debug("Setting DEVICE_FLASH_BASE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_FLASH_BASE, - this->targetParameters.flashStartAddress.value() - ); - } - - if (this->targetParameters.ramStartAddress.has_value()) { - Logger::debug("Setting DEVICE_SRAM_START AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_SRAM_START, - this->targetParameters.ramStartAddress.value() - ); - } - - if (this->targetParameters.eepromSize.has_value()) { - Logger::debug("Setting DEVICE_EEPROM_SIZE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_EEPROM_SIZE, - this->targetParameters.eepromSize.value() - ); - } - - if (this->targetParameters.eepromPageSize.has_value()) { - Logger::debug("Setting DEVICE_EEPROM_PAGE_SIZE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_EEPROM_PAGE_SIZE, - this->targetParameters.eepromPageSize.value() - ); - } - - if (this->targetParameters.ocdRevision.has_value()) { - Logger::debug("Setting DEVICE_OCD_REVISION AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_OCD_REVISION, - this->targetParameters.ocdRevision.value() - ); - } - - if (this->targetParameters.ocdDataRegister.has_value()) { - Logger::debug("Setting DEVICE_OCD_DATA_REGISTER AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_OCD_DATA_REGISTER, - this->targetParameters.ocdDataRegister.value() - ); - } - - if (this->targetParameters.spmcRegisterStartAddress.has_value()) { - Logger::debug("Setting DEVICE_SPMCR_REGISTER AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_SPMCR_REGISTER, - this->targetParameters.spmcRegisterStartAddress.value() - ); - } - - if (this->targetParameters.bootSectionStartAddress.has_value()) { - Logger::debug("Setting DEVICE_BOOT_START_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_BOOT_START_ADDR, - this->targetParameters.bootSectionStartAddress.value() - ); - } - - /* - * All addresses for registers that reside in the mapped IO memory segment include the mapped IO segment offset - * (start address). But the EDBG protocol requires *some* of these addresses to be stripped of this offset before - * sending them as target parameters. - * - * This applies to the following addresses: - * - * - OSCALL Address - * - EEARL Address - * - EEARH Address - * - EECR Address - * - EEDR Address - * - * It *doesn't* seem to apply to the SPMCR or OCDDR address. - */ - auto mappedIoStartAddress = this->targetParameters.mappedIoSegmentStartAddress.value_or(0); - - if (this->targetParameters.osccalAddress.has_value()) { - Logger::debug("Setting DEVICE_OSCCAL_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_OSCCAL_ADDR, - static_cast( + if (this->targetParameters.osccalAddress.has_value()) { + Logger::debug("Setting DEVICE_OSCCAL_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_OSCCAL_ADDR, + static_cast( this->targetParameters.osccalAddress.value() - mappedIoStartAddress ) - ); - } + ); + } - if (this->targetParameters.eepromAddressRegisterLow.has_value()) { - Logger::debug("Setting DEVICE_EEARL_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_EEARL_ADDR, - static_cast( + if (this->targetParameters.eepromAddressRegisterLow.has_value()) { + Logger::debug("Setting DEVICE_EEARL_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_EEARL_ADDR, + static_cast( this->targetParameters.eepromAddressRegisterLow.value() - mappedIoStartAddress ) - ); - } + ); + } - if (this->targetParameters.eepromAddressRegisterHigh.has_value()) { - Logger::debug("Setting DEVICE_EEARH_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_EEARH_ADDR, - static_cast( + if (this->targetParameters.eepromAddressRegisterHigh.has_value()) { + Logger::debug("Setting DEVICE_EEARH_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_EEARH_ADDR, + static_cast( this->targetParameters.eepromAddressRegisterHigh.value() - mappedIoStartAddress ) - ); - } + ); + } - if (this->targetParameters.eepromControlRegisterAddress.has_value()) { - Logger::debug("Setting DEVICE_EECR_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_EECR_ADDR, - static_cast( + if (this->targetParameters.eepromControlRegisterAddress.has_value()) { + Logger::debug("Setting DEVICE_EECR_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_EECR_ADDR, + static_cast( this->targetParameters.eepromControlRegisterAddress.value() - mappedIoStartAddress ) - ); - } + ); + } - if (this->targetParameters.eepromDataRegisterAddress.has_value()) { - Logger::debug("Setting DEVICE_EEDR_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_EEDR_ADDR, - static_cast( + if (this->targetParameters.eepromDataRegisterAddress.has_value()) { + Logger::debug("Setting DEVICE_EEDR_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_EEDR_ADDR, + static_cast( this->targetParameters.eepromDataRegisterAddress.value() - mappedIoStartAddress ) - ); - } -} - -void EdbgAvr8Interface::setPdiParameters() { - if (!this->targetParameters.appSectionPdiOffset.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: APPL_BASE_ADDR"); + ); + } } - if (!this->targetParameters.bootSectionPdiOffset.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: BOOT_BASE_ADDR"); - } + void EdbgAvr8Interface::setPdiParameters() { + if (!this->targetParameters.appSectionPdiOffset.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: APPL_BASE_ADDR"); + } - if (!this->targetParameters.bootSectionSize.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: BOOT_BYTES"); - } + if (!this->targetParameters.bootSectionPdiOffset.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: BOOT_BASE_ADDR"); + } - if (!this->targetParameters.flashSize.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: APPLICATION_BYTES"); - } + if (!this->targetParameters.bootSectionSize.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: BOOT_BYTES"); + } - if (!this->targetParameters.eepromPdiOffset.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: EEPROM_BASE_ADDR"); - } + if (!this->targetParameters.flashSize.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: APPLICATION_BYTES"); + } - if (!this->targetParameters.fuseRegistersPdiOffset.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: FUSE_BASE_ADDR"); - } + if (!this->targetParameters.eepromPdiOffset.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: EEPROM_BASE_ADDR"); + } - if (!this->targetParameters.lockRegistersPdiOffset.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: LOCKBIT_BASE_ADDR"); - } + if (!this->targetParameters.fuseRegistersPdiOffset.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: FUSE_BASE_ADDR"); + } - if (!this->targetParameters.userSignaturesPdiOffset.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: USER_SIGN_BASE_ADDR"); - } + if (!this->targetParameters.lockRegistersPdiOffset.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: LOCKBIT_BASE_ADDR"); + } - if (!this->targetParameters.productSignaturesPdiOffset.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: PROD_SIGN_BASE_ADDR"); - } + if (!this->targetParameters.userSignaturesPdiOffset.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: USER_SIGN_BASE_ADDR"); + } - if (!this->targetParameters.ramPdiOffset.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: DATA_BASE_ADDR"); - } + if (!this->targetParameters.productSignaturesPdiOffset.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: PROD_SIGN_BASE_ADDR"); + } - if (!this->targetParameters.flashPageSize.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: FLASH_PAGE_BYTES"); - } + if (!this->targetParameters.ramPdiOffset.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: DATA_BASE_ADDR"); + } - if (!this->targetParameters.eepromSize.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: EEPROM_SIZE"); - } + if (!this->targetParameters.flashPageSize.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: FLASH_PAGE_BYTES"); + } - if (!this->targetParameters.eepromPageSize.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: EEPROM_PAGE_SIZE"); - } + if (!this->targetParameters.eepromSize.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: EEPROM_SIZE"); + } - if (!this->targetParameters.nvmBaseAddress.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: NVM_BASE"); - } + if (!this->targetParameters.eepromPageSize.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: EEPROM_PAGE_SIZE"); + } - Logger::debug("Setting APPL_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_APPL_BASE_ADDR, - this->targetParameters.appSectionPdiOffset.value() - ); + if (!this->targetParameters.nvmBaseAddress.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: NVM_BASE"); + } - Logger::debug("Setting BOOT_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_BOOT_BASE_ADDR, - this->targetParameters.bootSectionPdiOffset.value() - ); - - Logger::debug("Setting EEPROM_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_EEPROM_BASE_ADDR, - this->targetParameters.eepromPdiOffset.value() - ); - - Logger::debug("Setting FUSE_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_FUSE_BASE_ADDR, - this->targetParameters.fuseRegistersPdiOffset.value() - ); - - Logger::debug("Setting LOCKBIT_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_LOCKBIT_BASE_ADDR, - this->targetParameters.lockRegistersPdiOffset.value() - ); - - Logger::debug("Setting USER_SIGN_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_USER_SIGN_BASE_ADDR, - this->targetParameters.userSignaturesPdiOffset.value() - ); - - Logger::debug("Setting PROD_SIGN_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_PROD_SIGN_BASE_ADDR, - this->targetParameters.productSignaturesPdiOffset.value() - ); - - Logger::debug("Setting DATA_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_DATA_BASE_ADDR, - this->targetParameters.ramPdiOffset.value() - ); - - Logger::debug("Setting APPLICATION_BYTES AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_APPLICATION_BYTES, - this->targetParameters.flashSize.value() - ); - - Logger::debug("Setting BOOT_BYTES AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_BOOT_BYTES, - this->targetParameters.bootSectionSize.value() - ); - - Logger::debug("Setting FLASH_PAGE_BYTES AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_FLASH_PAGE_BYTES, - this->targetParameters.flashPageSize.value() - ); - - Logger::debug("Setting EEPROM_SIZE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_EEPROM_SIZE, - this->targetParameters.eepromSize.value() - ); - - Logger::debug("Setting EEPROM_PAGE_SIZE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_EEPROM_PAGE_SIZE, - static_cast(this->targetParameters.eepromPageSize.value()) - ); - - Logger::debug("Setting NVM_BASE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_XMEGA_NVM_BASE, - this->targetParameters.nvmBaseAddress.value() - ); -} - -void EdbgAvr8Interface::setUpdiParameters() { - if (!this->targetParameters.signatureSegmentStartAddress.has_value()) { - throw DeviceInitializationFailure("Missing required parameter: SIGNATURE BASE ADDRESS"); - } - - if (this->targetParameters.programMemoryUpdiStartAddress.has_value()) { - /* - * The program memory base address field for UPDI sessions (DEVICE_UPDI_PROGMEM_BASE_ADDR) seems to be limited - * to two bytes in size, as opposed to the four byte size for the debugWire, JTAG and PDI equivalent fields. - * This is why, I suspect, another field was required for the most significant byte of the program memory base - * address (DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB). - * - * The additional DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB field is only one byte in size, so it brings the total - * capacity for the program memory base address to three bytes. Because of this, we ensure that all TDFs, for - * targets that support UPDI, specify an address that does not exceed the maximum value of a 24 bit unsigned - * integer. This is done in our TDF validation script (see src/Targets/TargetDescription/README.md for more). - */ - auto programMemBaseAddress = this->targetParameters.programMemoryUpdiStartAddress.value(); - Logger::debug("Setting DEVICE_UPDI_PROGMEM_BASE_ADDR AVR8 parameter"); + Logger::debug("Setting APPL_BASE_ADDR AVR8 parameter"); this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_PROGMEM_BASE_ADDR, - static_cast(programMemBaseAddress) + Avr8EdbgParameters::DEVICE_XMEGA_APPL_BASE_ADDR, + this->targetParameters.appSectionPdiOffset.value() ); - Logger::debug("Setting DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB AVR8 parameter"); + Logger::debug("Setting BOOT_BASE_ADDR AVR8 parameter"); this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB, - static_cast(programMemBaseAddress >> 16) - ); - } - - if (this->targetParameters.flashPageSize.has_value()) { - /* - * See the comment above regarding capacity limitations of the DEVICE_UPDI_PROGMEM_BASE_ADDR field. - * - * The same applies here, for the flash page size field (DEVICE_UPDI_FLASH_PAGE_SIZE). - */ - auto flashPageSize = this->targetParameters.flashPageSize.value(); - Logger::debug("Setting DEVICE_UPDI_FLASH_PAGE_SIZE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_FLASH_PAGE_SIZE, - static_cast(flashPageSize) + Avr8EdbgParameters::DEVICE_XMEGA_BOOT_BASE_ADDR, + this->targetParameters.bootSectionPdiOffset.value() ); - Logger::debug("Setting DEVICE_UPDI_FLASH_PAGE_SIZE_MSB AVR8 parameter"); + Logger::debug("Setting EEPROM_BASE_ADDR AVR8 parameter"); this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_FLASH_PAGE_SIZE_MSB, - static_cast(flashPageSize >> 8) + Avr8EdbgParameters::DEVICE_XMEGA_EEPROM_BASE_ADDR, + this->targetParameters.eepromPdiOffset.value() ); - } - if (this->targetParameters.eepromPageSize.has_value()) { - Logger::debug("Setting DEVICE_UPDI_EEPROM_PAGE_SIZE AVR8 parameter"); + Logger::debug("Setting FUSE_BASE_ADDR AVR8 parameter"); this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_EEPROM_PAGE_SIZE, - this->targetParameters.eepromPageSize.value() + Avr8EdbgParameters::DEVICE_XMEGA_FUSE_BASE_ADDR, + this->targetParameters.fuseRegistersPdiOffset.value() ); - } - if (this->targetParameters.nvmBaseAddress.has_value()) { - Logger::debug("Setting DEVICE_UPDI_NVMCTRL_ADDR AVR8 parameter"); + Logger::debug("Setting LOCKBIT_BASE_ADDR AVR8 parameter"); this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_NVMCTRL_ADDR, + Avr8EdbgParameters::DEVICE_XMEGA_LOCKBIT_BASE_ADDR, + this->targetParameters.lockRegistersPdiOffset.value() + ); + + Logger::debug("Setting USER_SIGN_BASE_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_XMEGA_USER_SIGN_BASE_ADDR, + this->targetParameters.userSignaturesPdiOffset.value() + ); + + Logger::debug("Setting PROD_SIGN_BASE_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_XMEGA_PROD_SIGN_BASE_ADDR, + this->targetParameters.productSignaturesPdiOffset.value() + ); + + Logger::debug("Setting DATA_BASE_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_XMEGA_DATA_BASE_ADDR, + this->targetParameters.ramPdiOffset.value() + ); + + Logger::debug("Setting APPLICATION_BYTES AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_XMEGA_APPLICATION_BYTES, + this->targetParameters.flashSize.value() + ); + + Logger::debug("Setting BOOT_BYTES AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_XMEGA_BOOT_BYTES, + this->targetParameters.bootSectionSize.value() + ); + + Logger::debug("Setting FLASH_PAGE_BYTES AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_XMEGA_FLASH_PAGE_BYTES, + this->targetParameters.flashPageSize.value() + ); + + Logger::debug("Setting EEPROM_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_XMEGA_EEPROM_SIZE, + this->targetParameters.eepromSize.value() + ); + + Logger::debug("Setting EEPROM_PAGE_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_XMEGA_EEPROM_PAGE_SIZE, + static_cast(this->targetParameters.eepromPageSize.value()) + ); + + Logger::debug("Setting NVM_BASE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_XMEGA_NVM_BASE, this->targetParameters.nvmBaseAddress.value() ); } - if (this->targetParameters.ocdModuleAddress.has_value()) { - Logger::debug("Setting DEVICE_UPDI_OCD_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_OCD_ADDR, - this->targetParameters.ocdModuleAddress.value() - ); - } - - if (this->targetParameters.flashSize.has_value()) { - Logger::debug("Setting DEVICE_UPDI_FLASH_SIZE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_FLASH_SIZE, - this->targetParameters.flashSize.value() - ); - } - - if (this->targetParameters.eepromSize.has_value()) { - Logger::debug("Setting DEVICE_UPDI_EEPROM_SIZE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_EEPROM_SIZE, - this->targetParameters.eepromSize.value() - ); - } - - if (this->targetParameters.eepromStartAddress.has_value()) { - Logger::debug("Setting DEVICE_UPDI_EEPROM_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_EEPROM_BASE_ADDR, - this->targetParameters.eepromStartAddress.value() - ); - } - - if (this->targetParameters.signatureSegmentStartAddress.has_value()) { - Logger::debug("Setting DEVICE_UPDI_SIG_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_SIG_BASE_ADDR, - this->targetParameters.signatureSegmentStartAddress.value() - ); - } - - if (this->targetParameters.fuseSegmentStartAddress.has_value()) { - Logger::debug("Setting DEVICE_UPDI_FUSE_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_FUSE_BASE_ADDR, - this->targetParameters.fuseSegmentStartAddress.value() - ); - } - - if (this->targetParameters.fuseSegmentSize.has_value()) { - Logger::debug("Setting DEVICE_UPDI_FUSE_SIZE AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_FUSE_SIZE, - this->targetParameters.fuseSegmentSize.value() - ); - } - - if (this->targetParameters.lockbitsSegmentStartAddress.has_value()) { - Logger::debug("Setting DEVICE_UPDI_LOCK_BASE_ADDR AVR8 parameter"); - this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_LOCK_BASE_ADDR, - this->targetParameters.lockbitsSegmentStartAddress.value() - ); - } - - this->setParameter( - Avr8EdbgParameters::DEVICE_UPDI_24_BIT_ADDRESSING_ENABLE, - this->targetParameters.programMemoryUpdiStartAddress.value_or(0) > 0xFFFF ? - static_cast(1) : static_cast(0) - ); -} - -void EdbgAvr8Interface::activatePhysical(bool applyExternalReset) { - auto commandFrame = CommandFrames::Avr8Generic::ActivatePhysical(applyExternalReset); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - if (!applyExternalReset) { - // Try again with external reset applied - Logger::debug("Failed to activate physical interface on AVR8 target " - "- retrying with external reset applied."); - return this->activatePhysical(true); + void EdbgAvr8Interface::setUpdiParameters() { + if (!this->targetParameters.signatureSegmentStartAddress.has_value()) { + throw DeviceInitializationFailure("Missing required parameter: SIGNATURE BASE ADDRESS"); } - throw Avr8CommandFailure("AVR8 Activate physical interface command failed", response); - } - - this->physicalInterfaceActivated = true; -} - -void EdbgAvr8Interface::deactivatePhysical() { - auto commandFrame = CommandFrames::Avr8Generic::DeactivatePhysical(); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Deactivate physical interface command failed", response); - } - - this->physicalInterfaceActivated = false; -} - -void EdbgAvr8Interface::attach() { - /* - * When attaching an ATmega target that is connected via JTAG, we must not set the breakAfterAttach flag, as this - * results in a timeout. - * - * However, in this case the attach command seems to _sometimes_ halt the target anyway, regardless of the - * value of the breakAfterAttach flag. So we still expect a stop event to be received shortly after issuing - * the attach command. - */ - auto commandFrame = CommandFrames::Avr8Generic::Attach( - this->configVariant != Avr8ConfigVariant::MEGAJTAG - ); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Attach command failed", response); - } - - this->targetAttached = true; - - try { - // Wait for stopped event - this->waitForStoppedEvent(); - - } catch (const Exception& exception) { - Logger::error("Execution on AVR8 target could not be halted post attach - " + exception.getMessage()); - } -} - -void EdbgAvr8Interface::detach() { - auto commandFrame = CommandFrames::Avr8Generic::Detach(); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Detach command failed", response); - } - - this->targetAttached = false; -} - -std::unique_ptr EdbgAvr8Interface::getAvrEvent() { - auto event = this->edbgInterface.requestAvrEvent(); - - if (!event.has_value()) { - return nullptr; - } - - switch (event->getEventId()) { - case AvrEventId::AVR8_BREAK_EVENT: { - // Break event - return std::make_unique(event.value()); - } - default: { + if (this->targetParameters.programMemoryUpdiStartAddress.has_value()) { /* - * TODO: This isn't very nice as we're performing an unnecessary copy. Maybe requestAvrEvents should - * return a unique_ptr instead? + * The program memory base address field for UPDI sessions (DEVICE_UPDI_PROGMEM_BASE_ADDR) seems to be + * limited to two bytes in size, as opposed to the four byte size for the debugWire, JTAG and PDI + * equivalent fields. This is why, I suspect, another field was required for the most significant byte of + * the program memory base address (DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB). + * + * The additional DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB field is only one byte in size, so it brings the total + * capacity for the program memory base address to three bytes. Because of this, we ensure that all TDFs, + * for targets that support UPDI, specify an address that does not exceed the maximum value of a 24 bit + * unsigned integer. This is done in our TDF validation script (see src/Targets/TargetDescription/README.md + * for more). */ - return std::make_unique(event.value()); + const auto programMemBaseAddress = this->targetParameters.programMemoryUpdiStartAddress.value(); + Logger::debug("Setting DEVICE_UPDI_PROGMEM_BASE_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_PROGMEM_BASE_ADDR, + static_cast(programMemBaseAddress) + ); + + Logger::debug("Setting DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB, + static_cast(programMemBaseAddress >> 16) + ); } + + if (this->targetParameters.flashPageSize.has_value()) { + /* + * See the comment above regarding capacity limitations of the DEVICE_UPDI_PROGMEM_BASE_ADDR field. + * + * The same applies here, for the flash page size field (DEVICE_UPDI_FLASH_PAGE_SIZE). + */ + auto flashPageSize = this->targetParameters.flashPageSize.value(); + Logger::debug("Setting DEVICE_UPDI_FLASH_PAGE_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_FLASH_PAGE_SIZE, + static_cast(flashPageSize) + ); + + Logger::debug("Setting DEVICE_UPDI_FLASH_PAGE_SIZE_MSB AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_FLASH_PAGE_SIZE_MSB, + static_cast(flashPageSize >> 8) + ); + } + + if (this->targetParameters.eepromPageSize.has_value()) { + Logger::debug("Setting DEVICE_UPDI_EEPROM_PAGE_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_EEPROM_PAGE_SIZE, + this->targetParameters.eepromPageSize.value() + ); + } + + if (this->targetParameters.nvmBaseAddress.has_value()) { + Logger::debug("Setting DEVICE_UPDI_NVMCTRL_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_NVMCTRL_ADDR, + this->targetParameters.nvmBaseAddress.value() + ); + } + + if (this->targetParameters.ocdModuleAddress.has_value()) { + Logger::debug("Setting DEVICE_UPDI_OCD_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_OCD_ADDR, + this->targetParameters.ocdModuleAddress.value() + ); + } + + if (this->targetParameters.flashSize.has_value()) { + Logger::debug("Setting DEVICE_UPDI_FLASH_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_FLASH_SIZE, + this->targetParameters.flashSize.value() + ); + } + + if (this->targetParameters.eepromSize.has_value()) { + Logger::debug("Setting DEVICE_UPDI_EEPROM_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_EEPROM_SIZE, + this->targetParameters.eepromSize.value() + ); + } + + if (this->targetParameters.eepromStartAddress.has_value()) { + Logger::debug("Setting DEVICE_UPDI_EEPROM_BASE_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_EEPROM_BASE_ADDR, + this->targetParameters.eepromStartAddress.value() + ); + } + + if (this->targetParameters.signatureSegmentStartAddress.has_value()) { + Logger::debug("Setting DEVICE_UPDI_SIG_BASE_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_SIG_BASE_ADDR, + this->targetParameters.signatureSegmentStartAddress.value() + ); + } + + if (this->targetParameters.fuseSegmentStartAddress.has_value()) { + Logger::debug("Setting DEVICE_UPDI_FUSE_BASE_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_FUSE_BASE_ADDR, + this->targetParameters.fuseSegmentStartAddress.value() + ); + } + + if (this->targetParameters.fuseSegmentSize.has_value()) { + Logger::debug("Setting DEVICE_UPDI_FUSE_SIZE AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_FUSE_SIZE, + this->targetParameters.fuseSegmentSize.value() + ); + } + + if (this->targetParameters.lockbitsSegmentStartAddress.has_value()) { + Logger::debug("Setting DEVICE_UPDI_LOCK_BASE_ADDR AVR8 parameter"); + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_LOCK_BASE_ADDR, + this->targetParameters.lockbitsSegmentStartAddress.value() + ); + } + + this->setParameter( + Avr8EdbgParameters::DEVICE_UPDI_24_BIT_ADDRESSING_ENABLE, + this->targetParameters.programMemoryUpdiStartAddress.value_or(0) > 0xFFFF ? + static_cast(1) : static_cast(0) + ); } -} -void EdbgAvr8Interface::clearEvents() { - while (this->getAvrEvent() != nullptr) {} -} + void EdbgAvr8Interface::activatePhysical(bool applyExternalReset) { + auto commandFrame = CommandFrames::Avr8Generic::ActivatePhysical(applyExternalReset); -TargetMemoryBuffer EdbgAvr8Interface::readMemory( - Avr8MemoryType type, - std::uint32_t startAddress, - std::uint32_t bytes, - const std::set& excludedAddresses -) { - if (!excludedAddresses.empty() && (this->avoidMaskedMemoryRead || type != Avr8MemoryType::SRAM)) { - /* - * Driver-side masked memory read. - * - * Split the read into numerous reads, whenever we encounter an excluded address. - * - * All values for bytes located at excluded addresses will be returned as 0x00 - this mirrors the behaviour - * of the masked read memory EDBG command. - */ - auto output = TargetMemoryBuffer(); - output.reserve(bytes); - - auto segmentStartAddress = startAddress; - const auto endAddress = startAddress + bytes - 1; - - for (const auto excludedAddress : excludedAddresses) { - if (excludedAddress < startAddress || excludedAddress > endAddress) { - // This excluded address is outside of the range from which we are reading, so it can be ignored. - continue; + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + if (!applyExternalReset) { + // Try again with external reset applied + Logger::debug("Failed to activate physical interface on AVR8 target " + "- retrying with external reset applied."); + return this->activatePhysical(true); } - const auto segmentSize = excludedAddress - segmentStartAddress; - if (segmentSize > 0) { + throw Avr8CommandFailure("AVR8 Activate physical interface command failed", response); + } + + this->physicalInterfaceActivated = true; + } + + void EdbgAvr8Interface::deactivatePhysical() { + auto commandFrame = CommandFrames::Avr8Generic::DeactivatePhysical(); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Deactivate physical interface command failed", response); + } + + this->physicalInterfaceActivated = false; + } + + void EdbgAvr8Interface::attach() { + /* + * When attaching an ATmega target that is connected via JTAG, we must not set the breakAfterAttach flag, as + * this results in a timeout. + * + * However, in this case the attach command seems to _sometimes_ halt the target anyway, regardless of the + * value of the breakAfterAttach flag. So we still expect a stop event to be received shortly after issuing + * the attach command. + */ + auto commandFrame = CommandFrames::Avr8Generic::Attach( + this->configVariant != Avr8ConfigVariant::MEGAJTAG + ); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Attach command failed", response); + } + + this->targetAttached = true; + + try { + // Wait for stopped event + this->waitForStoppedEvent(); + + } catch (const Exception& exception) { + Logger::error( + "Execution on AVR8 target could not be halted post attach - " + exception.getMessage() + ); + } + } + + void EdbgAvr8Interface::detach() { + auto commandFrame = CommandFrames::Avr8Generic::Detach(); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Detach command failed", response); + } + + this->targetAttached = false; + } + + std::unique_ptr EdbgAvr8Interface::getAvrEvent() { + auto event = this->edbgInterface.requestAvrEvent(); + + if (!event.has_value()) { + return nullptr; + } + + switch (event->getEventId()) { + case AvrEventId::AVR8_BREAK_EVENT: { + // Break event + return std::make_unique(event.value()); + } + default: { + /* + * TODO: This isn't very nice as we're performing an unnecessary copy. Maybe requestAvrEvents should + * return a unique_ptr instead? + */ + return std::make_unique(event.value()); + } + } + } + + void EdbgAvr8Interface::clearEvents() { + while (this->getAvrEvent() != nullptr) {} + } + + TargetMemoryBuffer EdbgAvr8Interface::readMemory( + Avr8MemoryType type, + std::uint32_t startAddress, + std::uint32_t bytes, + const std::set& excludedAddresses + ) { + if (!excludedAddresses.empty() && (this->avoidMaskedMemoryRead || type != Avr8MemoryType::SRAM)) { + /* + * Driver-side masked memory read. + * + * Split the read into numerous reads, whenever we encounter an excluded address. + * + * All values for bytes located at excluded addresses will be returned as 0x00 - this mirrors the behaviour + * of the masked read memory EDBG command. + */ + auto output = TargetMemoryBuffer(); + output.reserve(bytes); + + auto segmentStartAddress = startAddress; + const auto endAddress = startAddress + bytes - 1; + + for (const auto excludedAddress : excludedAddresses) { + if (excludedAddress < startAddress || excludedAddress > endAddress) { + // This excluded address is outside of the range from which we are reading, so it can be ignored. + continue; + } + + const auto segmentSize = excludedAddress - segmentStartAddress; + if (segmentSize > 0) { + auto segmentBuffer = this->readMemory( + type, + segmentStartAddress, + segmentSize + ); + + output.insert(output.end(), segmentBuffer.begin(), segmentBuffer.end()); + } + + output.emplace_back(0x00); + + segmentStartAddress = excludedAddress + 1; + } + + // Read final segment + const auto finalReadBytes = (endAddress - segmentStartAddress) + 1; + if (finalReadBytes > 0) { auto segmentBuffer = this->readMemory( type, segmentStartAddress, - segmentSize + finalReadBytes ); output.insert(output.end(), segmentBuffer.begin(), segmentBuffer.end()); } - output.emplace_back(0x00); - - segmentStartAddress = excludedAddress + 1; + return output; } - // Read final segment - const auto finalReadBytes = (endAddress - segmentStartAddress) + 1; - if (finalReadBytes > 0) { - auto segmentBuffer = this->readMemory( - type, - segmentStartAddress, - finalReadBytes - ); + if (type == Avr8MemoryType::FLASH_PAGE) { + // Flash reads must be done in pages + auto pageSize = this->targetParameters.flashPageSize.value_or(0); - output.insert(output.end(), segmentBuffer.begin(), segmentBuffer.end()); - } - - return output; - } - - if (type == Avr8MemoryType::FLASH_PAGE) { - // Flash reads must be done in pages - auto pageSize = this->targetParameters.flashPageSize.value_or(0); - - if (pageSize < 1 - || ( - this->maximumMemoryAccessSizePerRequest.has_value() - && pageSize > this->maximumMemoryAccessSizePerRequest - ) - ) { - throw Exception("Missing/invalid flash page size parameter"); - } - - if ((bytes % pageSize) != 0 || (startAddress % pageSize) != 0) { - /* - * The number of bytes to read and/or the start address are not aligned. - * - * Align both and call this function again. - */ - auto alignedAddress = startAddress; - auto alignedBytesToRead = bytes; - - if ((bytes % pageSize) != 0) { - auto pagesRequired = static_cast(std::ceil( - static_cast(bytes) / static_cast(pageSize) - )); - - alignedBytesToRead = (pagesRequired * pageSize); + if (pageSize < 1 + || ( + this->maximumMemoryAccessSizePerRequest.has_value() + && pageSize > this->maximumMemoryAccessSizePerRequest + ) + ) { + throw Exception("Missing/invalid flash page size parameter"); } - if ((startAddress % pageSize) != 0) { - alignedAddress = static_cast(std::floor( - static_cast(startAddress) / static_cast(pageSize) - ) * pageSize); + if ((bytes % pageSize) != 0 || (startAddress % pageSize) != 0) { + /* + * The number of bytes to read and/or the start address are not aligned. + * + * Align both and call this function again. + */ + auto alignedAddress = startAddress; + auto alignedBytesToRead = bytes; + + if ((bytes % pageSize) != 0) { + auto pagesRequired = static_cast(std::ceil( + static_cast(bytes) / static_cast(pageSize) + )); + + alignedBytesToRead = (pagesRequired * pageSize); + } + + if ((startAddress % pageSize) != 0) { + alignedAddress = static_cast(std::floor( + static_cast(startAddress) / static_cast(pageSize) + ) * pageSize); + + /* + * Given that we've pushed the start address back, this must be accounted for in the number of + * bytes to read. We'll need to include the difference as those are the bytes we're actually + * interested in. + * + * For example: + * + * Given: page size = 4 + * Given: dummy memory (each character represents one byte, with first byte at 0x00) = aaaabbbbccccdddd + * Given: requested start address = 0x05 + * Given: requested bytes to read = 4 + * + * The start address (0x05) would be: + * aaaabbbbccccdddd + * ^ + * Because only 4 bytes were requested, starting at address 0x05, we're only interested in the bytes + * at addresses 0x05, 0x06, 0x07 and 0x08 (that's bytes bbbc). + * + * But the start address isn't aligned, so we need to align it by pushing it back to the beginning + * of the page (so we'd set it to 0x04, for this example), which is what we do above, when setting + * alignedAddress: + * aaaabbbbccccdddd + * ^ + * But now we'll only be reading 4 bytes from start address 0x04, meaning we won't be reading + * that 4th byte (0x08). So we need to account for this by adding the difference of the requested + * start address and the aligned start address to the number of bytes to read, to ensure that we're + * reading all of the bytes that we're interested in. But this will throw off the aligned bytes!! + * So we need to also account for this by aligning the additional bytes before adding them to + * alignedBytesToRead. + * + * However, we could simply get away with just adding the bytes without aligning + * them (alignedBytesToRead += (address - alignedAddress);), as the subsequent recursive call will + * align them for us, but it will result in an unnecessary recursion, so we'll just align the + * additional bytes here. + */ + if ((startAddress - alignedAddress) > (alignedBytesToRead - bytes)) { + alignedBytesToRead += static_cast(std::ceil( + static_cast(startAddress - alignedAddress) / static_cast(pageSize) + )) * pageSize; + } + } /* - * Given that we've pushed the start address back, this must be accounted for in the number of - * bytes to read. We'll need to include the difference as those are the bytes we're actually - * interested in. - * - * For example: - * - * Given: page size = 4 - * Given: dummy memory (each character represents one byte, with first byte at 0x00) = aaaabbbbccccdddd - * Given: requested start address = 0x05 - * Given: requested bytes to read = 4 - * - * The start address (0x05) would be: - * aaaabbbbccccdddd - * ^ - * Because only 4 bytes were requested, starting at address 0x05, we're only interested in the bytes - * at addresses 0x05, 0x06, 0x07 and 0x08 (that's bytes bbbc). - * - * But the start address isn't aligned, so we need to align it by pushing it back to the beginning - * of the page (so we'd set it to 0x04, for this example), which is what we do above, when setting - * alignedAddress: - * aaaabbbbccccdddd - * ^ - * But now we'll only be reading 4 bytes from start address 0x04, meaning we won't be reading - * that 4th byte (0x08). So we need to account for this by adding the difference of the requested start - * address and the aligned start address to the number of bytes to read, to ensure that we're reading - * all of the bytes that we're interested in. But this will throw off the aligned bytes!! So we need - * to also account for this by aligning the additional bytes before adding them to alignedBytesToRead. - * - * However, we could simply get away with just adding the bytes without aligning - * them (alignedBytesToRead += (address - alignedAddress);), as the subsequent recursive call will - * align them for us, but it will result in an unnecessary recursion, so we'll just align the - * additional bytes here. + * Now that the start address and bytes to read have been aligned, we can simply invoke this function + * for a second time, with the aligned values. Then, return the requested data and discard the rest. */ - if ((startAddress - alignedAddress) > (alignedBytesToRead - bytes)) { - alignedBytesToRead += static_cast(std::ceil( - static_cast(startAddress - alignedAddress) / static_cast(pageSize) - )) * pageSize; + auto memoryBuffer = this->readMemory(type, alignedAddress, alignedBytesToRead, excludedAddresses); + return TargetMemoryBuffer( + memoryBuffer.begin() + (startAddress - alignedAddress), + memoryBuffer.begin() + (startAddress - alignedAddress) + bytes + ); + } + + // We can only read one flash page at a time. + if (bytes > pageSize) { + // bytes should always be a multiple of pageSize (given the code above) + assert(bytes % pageSize == 0); + int pagesRequired = static_cast(bytes / pageSize); + TargetMemoryBuffer memoryBuffer; + + for (auto i = 1; i <= pagesRequired; i++) { + auto pageBuffer = this->readMemory(type, startAddress + (pageSize * i), pageSize); + memoryBuffer.insert(memoryBuffer.end(), pageBuffer.begin(), pageBuffer.end()); } + + return memoryBuffer; } - /* - * Now that the start address and bytes to read have been aligned, we can simply invoke this function - * for a second time, with the aligned values. Then, return the requested data and discard the rest. - */ - auto memoryBuffer = this->readMemory(type, alignedAddress, alignedBytesToRead, excludedAddresses); - return TargetMemoryBuffer( - memoryBuffer.begin() + (startAddress - alignedAddress), - memoryBuffer.begin() + (startAddress - alignedAddress) + bytes - ); } - // We can only read one flash page at a time. - if (bytes > pageSize) { - // bytes should always be a multiple of pageSize (given the code above) - assert(bytes % pageSize == 0); - int pagesRequired = static_cast(bytes / pageSize); - TargetMemoryBuffer memoryBuffer; - - for (auto i = 1; i <= pagesRequired; i++) { - auto pageBuffer = this->readMemory(type, startAddress + (pageSize * i), pageSize); - memoryBuffer.insert(memoryBuffer.end(), pageBuffer.begin(), pageBuffer.end()); - } - - return memoryBuffer; - } - - } - - /* - * Enforce a maximum memory access request size. - * - * See the comment for EdbgAvr8Interface::setMaximumMemoryAccessSizePerRequest() for more on this. - */ - if (this->maximumMemoryAccessSizePerRequest.has_value() && bytes > this->maximumMemoryAccessSizePerRequest) { - auto maximumRequestSize = this->maximumMemoryAccessSizePerRequest.value(); - auto totalReadsRequired = std::ceil(static_cast(bytes) / static_cast(maximumRequestSize)); - auto output = std::vector(); - output.reserve(bytes); - - for (float i = 1; i <= totalReadsRequired; i++) { - auto bytesToRead = static_cast((bytes - output.size()) > maximumRequestSize ? - maximumRequestSize : bytes - output.size()); - auto data = this->readMemory( - type, - static_cast(startAddress + output.size()), - bytesToRead, - excludedAddresses - ); - output.insert(output.end(), data.begin(), data.end()); - } - - return output; - } - - if (type != Avr8MemoryType::FLASH_PAGE) { /* - * EDBG AVR8 debug tools behave in a really weird way when responding with more than two packets - * for a single read (non-flash) memory command. The data they return in this case appears to be of little use. + * Enforce a maximum memory access request size. * - * To address this, we make sure we only issue read memory commands that will result in no more than two - * response packets. For calls that require more than this, we simply split them into numerous calls. + * See the comment for EdbgAvr8Interface::setMaximumMemoryAccessSizePerRequest() for more on this. */ - - /* - * The subtraction of 20 bytes here is just to account for any other bytes included in the response - * that isn't actually the memory data (like the command ID, version bytes, etc). I could have sought the - * actual value but who has the time. It won't exceed 20 bytes. Bite me. - */ - auto singlePacketSize = static_cast(this->edbgInterface.getUsbHidInputReportSize() - 20); - auto totalResponsePackets = std::ceil(static_cast(bytes) / static_cast(singlePacketSize)); - auto totalReadsRequired = std::ceil(static_cast(totalResponsePackets) / 2); - - if (totalResponsePackets > 2) { - /* - * This call to readMemory() will result in more than two response packets, so split it into multiple calls - * that will result in no more than two response packets per call. - */ + if (this->maximumMemoryAccessSizePerRequest.has_value() && bytes > this->maximumMemoryAccessSizePerRequest) { + auto maximumRequestSize = this->maximumMemoryAccessSizePerRequest.value(); + auto totalReadsRequired = std::ceil(static_cast(bytes) / static_cast(maximumRequestSize)); auto output = std::vector(); + output.reserve(bytes); for (float i = 1; i <= totalReadsRequired; i++) { - auto bytesToRead = static_cast((bytes - output.size()) > (singlePacketSize * 2) ? - (singlePacketSize * 2) : bytes - output.size()); + auto bytesToRead = static_cast((bytes - output.size()) > maximumRequestSize ? + maximumRequestSize : bytes - output.size()); auto data = this->readMemory( type, static_cast(startAddress + output.size()), @@ -1440,71 +1414,115 @@ TargetMemoryBuffer EdbgAvr8Interface::readMemory( return output; } + + if (type != Avr8MemoryType::FLASH_PAGE) { + /* + * EDBG AVR8 debug tools behave in a really weird way when responding with more than two packets + * for a single read (non-flash) memory command. The data they return in this case appears to be of little + * use. + * + * To address this, we make sure we only issue read memory commands that will result in no more than two + * response packets. For calls that require more than this, we simply split them into numerous calls. + */ + + /* + * The subtraction of 20 bytes here is just to account for any other bytes included in the response + * that isn't actually the memory data (like the command ID, version bytes, etc). I could have sought the + * actual value but who has the time. It won't exceed 20 bytes. Bite me. + */ + auto singlePacketSize = static_cast(this->edbgInterface.getUsbHidInputReportSize() - 20); + auto totalResponsePackets = std::ceil(static_cast(bytes) / static_cast(singlePacketSize)); + auto totalReadsRequired = std::ceil(static_cast(totalResponsePackets) / 2); + + if (totalResponsePackets > 2) { + /* + * This call to readMemory() will result in more than two response packets, so split it into multiple + * calls that will result in no more than two response packets per call. + */ + auto output = std::vector(); + + for (float i = 1; i <= totalReadsRequired; i++) { + auto bytesToRead = static_cast( + (bytes - output.size()) > (singlePacketSize * 2) ? (singlePacketSize * 2) + : bytes - output.size() + ); + auto data = this->readMemory( + type, + static_cast(startAddress + output.size()), + bytesToRead, + excludedAddresses + ); + output.insert(output.end(), data.begin(), data.end()); + } + + return output; + } + } + + auto commandFrame = CommandFrames::Avr8Generic::ReadMemory(); + commandFrame.setType(type); + commandFrame.setAddress(startAddress); + commandFrame.setBytes(bytes); + commandFrame.setExcludedAddresses(excludedAddresses); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Read memory command failed", response); + } + + return response.getMemoryBuffer(); } - auto commandFrame = CommandFrames::Avr8Generic::ReadMemory(); - commandFrame.setType(type); - commandFrame.setAddress(startAddress); - commandFrame.setBytes(bytes); - commandFrame.setExcludedAddresses(excludedAddresses); + void EdbgAvr8Interface::writeMemory(Avr8MemoryType type, std::uint32_t address, const TargetMemoryBuffer& buffer) { + if (type == Avr8MemoryType::FLASH_PAGE) { + // TODO: Implement support for writing to flash + throw Exception("Writing to flash memory is not supported."); + } - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Read memory command failed", response); + auto commandFrame = CommandFrames::Avr8Generic::WriteMemory(); + commandFrame.setType(type); + commandFrame.setAddress(address); + commandFrame.setBuffer(buffer); + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Write memory command failed", response); + } } - return response.getMemoryBuffer(); -} + void EdbgAvr8Interface::refreshTargetState() { + auto avrEvent = this->getAvrEvent(); -void EdbgAvr8Interface::writeMemory(Avr8MemoryType type, std::uint32_t address, const TargetMemoryBuffer& buffer) { - if (type == Avr8MemoryType::FLASH_PAGE) { - // TODO: Implement support for writing to flash - throw Exception("Writing to flash memory is not supported."); + if (avrEvent != nullptr && avrEvent->getEventId() == AvrEventId::AVR8_BREAK_EVENT) { + auto* breakEvent = dynamic_cast(avrEvent.get()); + + if (breakEvent == nullptr) { + throw Exception("Failed to process AVR8 break event"); + } + + this->targetState = TargetState::STOPPED; + return; + } + + this->targetState = TargetState::RUNNING; } - auto commandFrame = CommandFrames::Avr8Generic::WriteMemory(); - commandFrame.setType(type); - commandFrame.setAddress(address); - commandFrame.setBuffer(buffer); + void EdbgAvr8Interface::disableDebugWire() { + auto commandFrame = CommandFrames::Avr8Generic::DisableDebugWire(); - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Write memory command failed", response); + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 Disable debugWire command failed", response); + } } -} -void EdbgAvr8Interface::refreshTargetState() { - auto avrEvent = this->getAvrEvent(); - - if (avrEvent != nullptr && avrEvent->getEventId() == AvrEventId::AVR8_BREAK_EVENT) { - auto* breakEvent = dynamic_cast(avrEvent.get()); + void EdbgAvr8Interface::waitForStoppedEvent() { + auto breakEvent = this->waitForAvrEvent(); if (breakEvent == nullptr) { - throw Exception("Failed to process AVR8 break event"); + throw Exception("Failed to receive break event for AVR8 target"); } this->targetState = TargetState::STOPPED; - return; - } - - this->targetState = TargetState::RUNNING; -} - -void EdbgAvr8Interface::disableDebugWire() { - auto commandFrame = CommandFrames::Avr8Generic::DisableDebugWire(); - - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame(commandFrame); - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Disable debugWire command failed", response); } } - -void EdbgAvr8Interface::waitForStoppedEvent() { - auto breakEvent = this->waitForAvrEvent(); - - if (breakEvent == nullptr) { - throw Exception("Failed to receive break event for AVR8 target"); - } - - this->targetState = TargetState::STOPPED; -} diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/Events/AVR8Generic/BreakEvent.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/Events/AVR8Generic/BreakEvent.cpp index 778ab147..e7464ccd 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/Events/AVR8Generic/BreakEvent.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/Events/AVR8Generic/BreakEvent.cpp @@ -2,28 +2,33 @@ #include "src/Exceptions/Exception.hpp" -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Targets; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr +{ + using namespace Bloom::Exceptions; -void BreakEvent::init(const AvrEvent& event) { - AvrEvent::init(event); - auto& data = this->getEventData(); + using Bloom::Targets::TargetBreakCause; - if (data.size() < 8) { - /* - * All BreakEvent packets must consist of at least 9 bytes: - * 1 byte for event ID - * 4 bytes for program counter - * 1 byte for break cause - * 2 bytes for extended info - */ - throw Exception("Failed to process BreakEvent from AvrEvent - unexpected packet size."); + void BreakEvent::init(const AvrEvent& event) { + AvrEvent::init(event); + const auto& data = this->getEventData(); + + if (data.size() < 8) { + /* + * All BreakEvent packets must consist of at least 9 bytes: + * 1 byte for event ID + * 4 bytes for program counter + * 1 byte for break cause + * 2 bytes for extended info + */ + throw Exception("Failed to process BreakEvent from AvrEvent - unexpected packet size."); + } + + // Program counter consists of 4 bytes + this->programCounter = static_cast( + (data[4] << 24) | (data[3] << 16) | (data[2] << 8) | data[1] + ) * 2; + + // Break cause is 1 byte, where 0x01 is 'program breakpoint' and 0x00 'unspecified' + this->breakCause = data[7] == 0x01 ? TargetBreakCause::BREAKPOINT : TargetBreakCause::UNKNOWN; } - - // Program counter consists of 4 bytes - this->programCounter = static_cast((data[4] << 24) | (data[3] << 16) | (data[2] << 8) | data[1]) * 2; - - // Break cause is 1 byte, where 0x01 is 'program breakpoint' and 0x00 'unspecified' - this->breakCause = data[7] == 0x01 ? TargetBreakCause::BREAKPOINT : TargetBreakCause::UNKNOWN; } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/ResponseFrames/AvrResponseFrame.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/ResponseFrames/AvrResponseFrame.cpp index a03d0bd8..e087930d 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/ResponseFrames/AvrResponseFrame.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/ResponseFrames/AvrResponseFrame.cpp @@ -2,36 +2,38 @@ #include "src/Exceptions/Exception.hpp" -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr +{ + using namespace Bloom::Exceptions; -void AvrResponseFrame::initFromAvrResponses(const std::vector& avrResponses) { - // Build a raw frame buffer from the AVRResponse objects and just call initFromRawFrame() - std::vector rawFrame; + void AvrResponseFrame::initFromAvrResponses(const std::vector& avrResponses) { + // Build a raw frame buffer from the AVRResponse objects and just call initFromRawFrame() + std::vector rawFrame; - for (auto& avrResponse : avrResponses) { - auto responsePacket = avrResponse.getResponsePacket(); - rawFrame.insert(rawFrame.end(), responsePacket.begin(), responsePacket.end()); + for (const auto& avrResponse : avrResponses) { + auto responsePacket = avrResponse.getResponsePacket(); + rawFrame.insert(rawFrame.end(), responsePacket.begin(), responsePacket.end()); + } + + return this->initFromRawFrame(rawFrame); } - return this->initFromRawFrame(rawFrame); -} - -void AvrResponseFrame::initFromRawFrame(const std::vector& rawFrame) { - if (rawFrame.size() < 4) { - // All AVR response frames must consist of at least four bytes (SOF, sequence ID (two bytes) and - // a protocol handler ID) - throw Exception("Failed to construct AvrResponseFrame - unexpected end to raw frame"); - } - - if (rawFrame[0] != 0x0E) { - // The SOF field must always be 0x0E - throw Exception("Failed to construct AvrResponseFrame - unexpected SOF field value in raw frame"); - } - - this->setSequenceId(static_cast((rawFrame[2] << 8) + rawFrame[1])); - this->setProtocolHandlerId(rawFrame[3]); - - auto& payload = this->getPayload(); - payload.insert(payload.begin(), rawFrame.begin() + 4, rawFrame.end()); + void AvrResponseFrame::initFromRawFrame(const std::vector& rawFrame) { + if (rawFrame.size() < 4) { + // All AVR response frames must consist of at least four bytes (SOF, sequence ID (two bytes) and + // a protocol handler ID) + throw Exception("Failed to construct AvrResponseFrame - unexpected end to raw frame"); + } + + if (rawFrame[0] != 0x0E) { + // The SOF field must always be 0x0E + throw Exception("Failed to construct AvrResponseFrame - unexpected SOF field value in raw frame"); + } + + this->setSequenceId(static_cast((rawFrame[2] << 8) + rawFrame[1])); + this->setProtocolHandlerId(rawFrame[3]); + + auto& payload = this->getPayload(); + payload.insert(payload.begin(), rawFrame.begin() + 4, rawFrame.end()); + } } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/EdbgInterface.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/EdbgInterface.cpp index 14ff73ed..13fe4583 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/EdbgInterface.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/EdbgInterface.cpp @@ -4,95 +4,99 @@ #include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp" -using namespace Bloom::DebugToolDrivers; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg; -using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; -using namespace Bloom::Exceptions; -using namespace Bloom::Exceptions; +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg +{ + using namespace Bloom::Exceptions; -Protocols::CmsisDap::Response EdbgInterface::sendAvrCommandFrameAndWaitForResponse( - const Protocols::CmsisDap::Edbg::Avr::AvrCommandFrame& avrCommandFrame -) { - // An AVR command frame can be split into multiple CMSIS-DAP commands. Each command - // containing a fragment of the AvrCommandFrame. + Protocols::CmsisDap::Response EdbgInterface::sendAvrCommandFrameAndWaitForResponse( + const Protocols::CmsisDap::Edbg::Avr::AvrCommandFrame& avrCommandFrame + ) { + // An AVR command frame can be split into multiple CMSIS-DAP commands. Each command + // containing a fragment of the AvrCommandFrame. - // Minus 3 to accommodate AVR command meta data - std::size_t maximumCommandPacketSize = (this->getUsbHidInputReportSize() - 3); + // Minus 3 to accommodate AVR command meta data + std::size_t maximumCommandPacketSize = (this->getUsbHidInputReportSize() - 3); - auto avrCommands = avrCommandFrame.generateAvrCommands(maximumCommandPacketSize); + auto avrCommands = avrCommandFrame.generateAvrCommands(maximumCommandPacketSize); - for (auto& avrCommand : avrCommands) { - // Send command to device - auto response = this->sendCommandAndWaitForResponse(avrCommand); + for (auto& avrCommand : avrCommands) { + // Send command to device + auto response = this->sendCommandAndWaitForResponse(avrCommand); - if (&avrCommand ==& avrCommands.back()) { - return* response; + if (&avrCommand == &avrCommands.back()) { + return *response; + } } + + // This should never happen + throw DeviceCommunicationFailure( + "Cannot send AVR command frame - failed to generate CMSIS-DAP Vendor (AVR) commands" + ); } - // This should never happen - throw DeviceCommunicationFailure( - "Cannot send AVR command frame - failed to generate CMSIS-DAP Vendor (AVR) commands" - ); -} + Protocols::CmsisDap::Edbg::Avr::AvrResponse EdbgInterface::getAvrResponse() { + auto cmsisResponse = this->getResponse(); -Protocols::CmsisDap::Edbg::Avr::AvrResponse EdbgInterface::getAvrResponse() { - auto cmsisResponse = this->getResponse(); + if (cmsisResponse->getResponseId() == 0x81) { + // This is an AVR_RSP response + auto avrResponse = Protocols::CmsisDap::Edbg::Avr::AvrResponse(); + avrResponse.init(*cmsisResponse); + return avrResponse; + } - if (cmsisResponse->getResponseId() == 0x81) { - // This is an AVR_RSP response - auto avrResponse = Protocols::CmsisDap::Edbg::Avr::AvrResponse(); - avrResponse.init(*cmsisResponse); - return avrResponse; - } else { throw DeviceCommunicationFailure("Unexpected response to AvrResponseCommand from device"); } -} -std::optional EdbgInterface::requestAvrEvent() { - this->sendCommand(AvrEventCommand()); - auto cmsisResponse = this->getResponse(); + std::optional EdbgInterface::requestAvrEvent() { + this->sendCommand(AvrEventCommand()); + auto cmsisResponse = this->getResponse(); + + if (cmsisResponse->getResponseId() == 0x82) { + // This is an AVR_EVT response + auto avrEvent = Protocols::CmsisDap::Edbg::Avr::AvrEvent(); + avrEvent.init(*cmsisResponse); + return avrEvent.getEventDataSize() > 0 ? std::optional(avrEvent) : std::nullopt; + } - if (cmsisResponse->getResponseId() == 0x82) { - // This is an AVR_EVT response - auto avrEvent = Protocols::CmsisDap::Edbg::Avr::AvrEvent(); - avrEvent.init(*cmsisResponse); - return avrEvent.getEventDataSize() > 0 ? std::optional(avrEvent): std::nullopt; - } else { throw DeviceCommunicationFailure("Unexpected response to AvrEventCommand from device"); } -} -std::vector EdbgInterface::requestAvrResponses() { - std::vector responses; - AvrResponseCommand responseCommand; + std::vector EdbgInterface::requestAvrResponses() { + using Protocols::CmsisDap::Edbg::Avr::AvrResponseCommand; - this->sendCommand(responseCommand); - auto response = this->getAvrResponse(); - responses.push_back(response); - int fragmentCount = response.getFragmentCount(); + std::vector responses; + AvrResponseCommand responseCommand; - while (responses.size() < fragmentCount) { - // There are more response packets this->sendCommand(responseCommand); - response = this->getAvrResponse(); - - if (response.getFragmentCount() != fragmentCount) { - throw DeviceCommunicationFailure( - "Failed to fetch AVRResponse objects - invalid fragment count returned." - ); - } - - if (response.getFragmentCount() == 0 && response.getFragmentNumber() == 0) { - throw DeviceCommunicationFailure("Failed to fetch AVRResponse objects - unexpected empty response"); - - } else if (response.getFragmentNumber() == 0) { - // End of response data ( &this packet can be ignored) - break; - } - + auto response = this->getAvrResponse(); responses.push_back(response); - } + int fragmentCount = response.getFragmentCount(); - return responses; + while (responses.size() < fragmentCount) { + // There are more response packets + this->sendCommand(responseCommand); + response = this->getAvrResponse(); + + if (response.getFragmentCount() != fragmentCount) { + throw DeviceCommunicationFailure( + "Failed to fetch AVRResponse objects - invalid fragment count returned." + ); + } + + if (response.getFragmentCount() == 0 && response.getFragmentNumber() == 0) { + throw DeviceCommunicationFailure( + "Failed to fetch AVRResponse objects - unexpected empty response" + ); + } + + if (response.getFragmentNumber() == 0) { + // End of response data ( &this packet can be ignored) + break; + } + + responses.push_back(response); + } + + return responses; + } } diff --git a/src/DebugToolDrivers/USB/HID/HidInterface.cpp b/src/DebugToolDrivers/USB/HID/HidInterface.cpp index 057fa8eb..04dbf292 100644 --- a/src/DebugToolDrivers/USB/HID/HidInterface.cpp +++ b/src/DebugToolDrivers/USB/HID/HidInterface.cpp @@ -5,127 +5,133 @@ #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" #include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp" -using namespace Bloom::Usb; -using namespace Bloom::Exceptions; +namespace Bloom::Usb +{ + using namespace Bloom::Exceptions; -void HidInterface::init() { - if (this->libUsbDevice == nullptr) { - throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer."); - } - - hid_init(); - hid_device* hidDevice = nullptr; - - std::string hidInterfacePath = this->getDevicePathByInterfaceNumber(this->getNumber()); - Logger::debug("HID device path: " + hidInterfacePath); - - if ((hidDevice = hid_open_path(hidInterfacePath.c_str())) == nullptr) { - throw DeviceInitializationFailure("Failed to open HID device via hidapi."); - } - - if (hidDevice->input_ep_max_packet_size < 1) { - throw DeviceInitializationFailure( - "Invalid max packet size for USB endpoint, on interface " + std::to_string(this->getNumber()) - ); - } - - this->setHidDevice(hidDevice); - this->setInputReportSize(static_cast(hidDevice->input_ep_max_packet_size)); - this->libUsbDeviceHandle = hidDevice->device_handle; - this->initialised = true; -} - -void HidInterface::close() { - auto* hidDevice = this->getHidDevice(); - - if (hidDevice != nullptr) { - this->libUsbDeviceHandle = nullptr; - hid_close(hidDevice); - // hid_close() releases the interface - this->claimed = false; - } - - hid_exit(); - Interface::close(); -} - -std::vector HidInterface::read(unsigned int timeout) { - std::vector output; - auto readSize = this->getInputReportSize(); - - // Attempt to read the first HID report packet, and whatever is left after that. - output.resize(readSize); - auto transferredByteCount = this->read(output.data(), readSize, timeout); - auto totalByteCount = transferredByteCount; - - while (transferredByteCount >= readSize) { - output.resize(totalByteCount + readSize); - - transferredByteCount = this->read(output.data() + totalByteCount, readSize, 1); - totalByteCount += transferredByteCount; - } - - output.resize(totalByteCount); - return output; -} - -void HidInterface::write(std::vector buffer) { - if (buffer.size() > this->getInputReportSize()) { - throw DeviceCommunicationFailure( - "Cannot send data via HID interface - data exceeds maximum packet size." - ); - } - - if (buffer.size() < this->getInputReportSize()) { - /* - * Every report we send via the USB HID interface should be of a fixed size. - * In the event of a report being too small, we just fill the buffer vector with 0. - */ - buffer.resize(this->getInputReportSize(), 0); - } - - int transferred = 0; - auto length = buffer.size(); - - if ((transferred = hid_write(this->getHidDevice(), buffer.data(), length)) != length) { - Logger::debug("Attempted to write " + std::to_string(length) - + " bytes to HID interface. Bytes written: " + std::to_string(transferred)); - throw DeviceCommunicationFailure("Failed to write data to HID interface."); - } -} - -std::size_t HidInterface::read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout) { - int transferred; - - if ((transferred = hid_read_timeout( - this->hidDevice, - buffer, - maxLength, - timeout == 0 ? -1 : static_cast(timeout)) - ) == -1 - ) { - throw DeviceCommunicationFailure("Failed to read from HID device."); - } - - return static_cast(transferred); -} - -std::string HidInterface::getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber) { - hid_device_info* hidDeviceInfoList = hid_enumerate(this->getVendorId(), this->getProductId()); - - while (hidDeviceInfoList != nullptr) { - if (hidDeviceInfoList->interface_number == interfaceNumber) { - break; + void HidInterface::init() { + if (this->libUsbDevice == nullptr) { + throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer."); } - hidDeviceInfoList = hidDeviceInfoList->next; + hid_init(); + hid_device* hidDevice = nullptr; + + std::string hidInterfacePath = this->getDevicePathByInterfaceNumber(this->getNumber()); + Logger::debug("HID device path: " + hidInterfacePath); + + if ((hidDevice = hid_open_path(hidInterfacePath.c_str())) == nullptr) { + throw DeviceInitializationFailure("Failed to open HID device via hidapi."); + } + + if (hidDevice->input_ep_max_packet_size < 1) { + throw DeviceInitializationFailure( + "Invalid max packet size for USB endpoint, on interface " + + std::to_string(this->getNumber()) + ); + } + + this->setHidDevice(hidDevice); + this->setInputReportSize(static_cast(hidDevice->input_ep_max_packet_size)); + this->libUsbDeviceHandle = hidDevice->device_handle; + this->initialised = true; } - if (hidDeviceInfoList == nullptr) { - throw DeviceInitializationFailure("Failed to match interface number with HID interface."); + void HidInterface::close() { + auto* hidDevice = this->getHidDevice(); + + if (hidDevice != nullptr) { + this->libUsbDeviceHandle = nullptr; + hid_close(hidDevice); + // hid_close() releases the interface + this->claimed = false; + } + + hid_exit(); + Interface::close(); } - auto path = std::string(hidDeviceInfoList->path); - hid_free_enumeration(hidDeviceInfoList); - return path; + std::vector HidInterface::read(unsigned int timeout) { + std::vector output; + auto readSize = this->getInputReportSize(); + + // Attempt to read the first HID report packet, and whatever is left after that. + output.resize(readSize); + auto transferredByteCount = this->read(output.data(), readSize, timeout); + auto totalByteCount = transferredByteCount; + + while (transferredByteCount >= readSize) { + output.resize(totalByteCount + readSize); + + transferredByteCount = this->read(output.data() + totalByteCount, readSize, 1); + totalByteCount += transferredByteCount; + } + + output.resize(totalByteCount); + return output; + } + + void HidInterface::write(std::vector buffer) { + if (buffer.size() > this->getInputReportSize()) { + throw DeviceCommunicationFailure( + "Cannot send data via HID interface - data exceeds maximum packet size." + ); + } + + if (buffer.size() < this->getInputReportSize()) { + /* + * Every report we send via the USB HID interface should be of a fixed size. + * In the event of a report being too small, we just fill the buffer vector with 0. + */ + buffer.resize(this->getInputReportSize(), 0); + } + + int transferred = 0; + auto length = buffer.size(); + + if ((transferred = hid_write(this->getHidDevice(), buffer.data(), length)) != length) { + Logger::debug("Attempted to write " + std::to_string(length) + + " bytes to HID interface. Bytes written: " + std::to_string(transferred)); + throw DeviceCommunicationFailure("Failed to write data to HID interface."); + } + } + + std::size_t HidInterface::read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout) { + int transferred; + + if ((transferred = hid_read_timeout( + this->hidDevice, + buffer, + maxLength, + timeout == 0 ? -1 : static_cast(timeout)) + ) == -1 + ) { + throw DeviceCommunicationFailure("Failed to read from HID device."); + } + + return static_cast(transferred); + } + + std::string HidInterface::getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber) { + hid_device_info* hidDeviceInfoList = hid_enumerate( + this->getVendorId(), + this->getProductId() + ); + + while (hidDeviceInfoList != nullptr) { + if (hidDeviceInfoList->interface_number == interfaceNumber) { + break; + } + + hidDeviceInfoList = hidDeviceInfoList->next; + } + + if (hidDeviceInfoList == nullptr) { + throw DeviceInitializationFailure("Failed to match interface number with HID interface."); + } + + auto path = std::string(hidDeviceInfoList->path); + hid_free_enumeration(hidDeviceInfoList); + return path; + } } diff --git a/src/DebugToolDrivers/USB/Interface.cpp b/src/DebugToolDrivers/USB/Interface.cpp index 0339d8b7..73445cf9 100644 --- a/src/DebugToolDrivers/USB/Interface.cpp +++ b/src/DebugToolDrivers/USB/Interface.cpp @@ -1,121 +1,123 @@ #include "Interface.hpp" #include -#include #include "src/TargetController/Exceptions/DeviceFailure.hpp" #include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -using namespace Bloom::Usb; -using namespace Bloom::Exceptions; +namespace Bloom::Usb +{ + using namespace Bloom::Exceptions; -void Interface::init() { - if (this->libUsbDevice == nullptr) { - throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer."); - } - - if (this->libUsbDeviceHandle == nullptr) { - throw DeviceInitializationFailure("Cannot initialise interface without libusb device handle."); - } - - this->initialised = true; -} - -void Interface::close() { - if (this->libUsbDeviceHandle != nullptr) { - this->release(); - } - - this->initialised = false; -} - -void Interface::claim() { - int interfaceNumber = this->getNumber(); - - this->detachKernelDriver(); - - if (libusb_claim_interface(this->libUsbDeviceHandle, interfaceNumber) != 0) { - throw DeviceInitializationFailure( - "Failed to claim interface {" + std::to_string(interfaceNumber) + "} on USB device\n" - ); - } - - this->claimed = true; -} - -void Interface::detachKernelDriver() { - int interfaceNumber = this->getNumber(); - int libUsbStatusCode; - - if ((libUsbStatusCode = libusb_kernel_driver_active(this->libUsbDeviceHandle, interfaceNumber)) != 0) { - if (libUsbStatusCode == 1) { - // A kernel driver is active on this interface. Attempt to detach it - if (libusb_detach_kernel_driver(this->libUsbDeviceHandle, interfaceNumber) != 0) { - throw DeviceInitializationFailure("Failed to detach kernel driver from interface " + - std::to_string(interfaceNumber) + "\n"); - } - } else { - throw DeviceInitializationFailure("Failed to check for active kernel driver on USB interface."); + void Interface::init() { + if (this->libUsbDevice == nullptr) { + throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer."); } - } -} -void Interface::release() { - if (this->isClaimed()) { - if (libusb_release_interface(this->libUsbDeviceHandle, this->getNumber()) != 0) { - throw DeviceFailure( - "Failed to release interface {" + std::to_string(this->getNumber()) + "} on USB device\n" + if (this->libUsbDeviceHandle == nullptr) { + throw DeviceInitializationFailure("Cannot initialise interface without libusb device handle."); + } + + this->initialised = true; + } + + void Interface::close() { + if (this->libUsbDeviceHandle != nullptr) { + this->release(); + } + + this->initialised = false; + } + + void Interface::claim() { + int interfaceNumber = this->getNumber(); + + this->detachKernelDriver(); + + if (libusb_claim_interface(this->libUsbDeviceHandle, interfaceNumber) != 0) { + throw DeviceInitializationFailure( + "Failed to claim interface {" + std::to_string(interfaceNumber) + "} on USB device\n" ); } - this->claimed = false; + this->claimed = true; } -} -int Interface::read(unsigned char* buffer, unsigned char endPoint, size_t length, size_t timeout) { - int totalTransferred = 0; - int transferred = 0; - int libUsbStatusCode = 0; + void Interface::detachKernelDriver() { + int interfaceNumber = this->getNumber(); + int libUsbStatusCode; + + if ((libUsbStatusCode = libusb_kernel_driver_active(this->libUsbDeviceHandle, interfaceNumber)) != 0) { + if (libUsbStatusCode == 1) { + // A kernel driver is active on this interface. Attempt to detach it + if (libusb_detach_kernel_driver(this->libUsbDeviceHandle, interfaceNumber) != 0) { + throw DeviceInitializationFailure("Failed to detach kernel driver from interface " + + std::to_string(interfaceNumber) + "\n"); + } + } else { + throw DeviceInitializationFailure("Failed to check for active kernel driver on USB interface."); + } + } + } + + void Interface::release() { + if (this->isClaimed()) { + if (libusb_release_interface(this->libUsbDeviceHandle, this->getNumber()) != 0) { + throw DeviceFailure( + "Failed to release interface {" + std::to_string(this->getNumber()) + + "} on USB device\n" + ); + } + + this->claimed = false; + } + } + + int Interface::read(unsigned char* buffer, unsigned char endPoint, size_t length, size_t timeout) { + int totalTransferred = 0; + int transferred = 0; + int libUsbStatusCode = 0; + + while (length > totalTransferred) { + libUsbStatusCode = libusb_interrupt_transfer( + this->libUsbDeviceHandle, + endPoint, + buffer, + static_cast(length), + &transferred, + static_cast(timeout) + ); + + if (libUsbStatusCode != 0 && libUsbStatusCode != -7) { + throw DeviceCommunicationFailure( + "Failed to read from USB device. Error code returned: " + std::to_string(libUsbStatusCode) + ); + } + + totalTransferred += transferred; + } + + return transferred; + } + + void Interface::write(unsigned char* buffer, unsigned char endPoint, int length) { + int transferred = 0; + int libUsbStatusCode = 0; - while (length > totalTransferred) { libUsbStatusCode = libusb_interrupt_transfer( this->libUsbDeviceHandle, endPoint, buffer, - static_cast(length), + length, &transferred, - static_cast(timeout) + 0 ); - if (libUsbStatusCode != 0 && libUsbStatusCode != -7) { + if (libUsbStatusCode != 0) { throw DeviceCommunicationFailure( "Failed to read from USB device. Error code returned: " + std::to_string(libUsbStatusCode) ); } - - totalTransferred += transferred; - } - - return transferred; -} - -void Interface::write(unsigned char* buffer, unsigned char endPoint, int length) { - int transferred = 0; - int libUsbStatusCode = 0; - - libUsbStatusCode = libusb_interrupt_transfer( - this->libUsbDeviceHandle, - endPoint, - buffer, - length, - &transferred, - 0 - ); - - if (libUsbStatusCode != 0) { - throw DeviceCommunicationFailure( - "Failed to read from USB device. Error code returned: " + std::to_string(libUsbStatusCode) - ); } } diff --git a/src/DebugToolDrivers/USB/UsbDevice.cpp b/src/DebugToolDrivers/USB/UsbDevice.cpp index 9b259926..3080872f 100644 --- a/src/DebugToolDrivers/USB/UsbDevice.cpp +++ b/src/DebugToolDrivers/USB/UsbDevice.cpp @@ -3,102 +3,107 @@ #include "src/Logger/Logger.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -using Bloom::Usb::UsbDevice; -using namespace Bloom::Exceptions; +namespace Bloom::Usb +{ + using namespace Bloom::Exceptions; -void UsbDevice::init() { - libusb_init(&this->libUsbContext); -// libusb_set_option(this->libUsbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE); - auto devices = this->findMatchingDevices(); + void UsbDevice::init() { + libusb_init(&this->libUsbContext); +// libusb_set_option(this->libUsbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE); + auto devices = this->findMatchingDevices(); - if (devices.empty()) { - throw DeviceInitializationFailure("Failed to find USB device with matching vendor & product ID."); - - } else if (devices.size() > 1) { - // TODO: implement support for multiple devices (maybe via serial number?) - throw DeviceInitializationFailure( - "Numerous devices of matching vendor & product ID found.\n" - "Please ensure that only one debug tool is connected and then try again." - ); - } - - // For now, just use the first device found. - auto device = devices.front(); - this->setLibUsbDevice(device); - - int libUsbStatusCode; - - // Obtain a device handle from libusb - if ((libUsbStatusCode = libusb_open(libUsbDevice, &this->libUsbDeviceHandle)) < 0) { - throw DeviceInitializationFailure( - "Failed to open USB device - error code " + std::to_string(libUsbStatusCode) + " returned." - ); - } -} - -void UsbDevice::setConfiguration(int configIndex) { - libusb_config_descriptor* configDescriptor = {}; - int libUsbStatusCode; - - if ((libUsbStatusCode = libusb_get_config_descriptor(this->libUsbDevice, 0, &configDescriptor))) { - throw DeviceInitializationFailure( - "Failed to obtain USB configuration descriptor - error code " + std::to_string(libUsbStatusCode) - + " returned." - ); - } - - if ((libUsbStatusCode = libusb_set_configuration(this->libUsbDeviceHandle, configDescriptor->bConfigurationValue))) { - throw DeviceInitializationFailure( - "Failed to set USB configuration - error code " + std::to_string(libUsbStatusCode) + " returned." - ); - } - - libusb_free_config_descriptor(configDescriptor); -} - -std::vector UsbDevice::findMatchingDevices( - std::optional vendorId, std::optional productId -) { - auto libUsbContext = this->libUsbContext; - libusb_device** devices = nullptr; - libusb_device* device; - std::vector matchedDevices; - ssize_t i = 0, libUsbStatusCode; - - auto vendorIdToMatch = vendorId.value_or(this->vendorId); - auto productIdToMatch = productId.value_or(this->productId); - - if ((libUsbStatusCode = libusb_get_device_list(libUsbContext, &devices)) < 0) { - throw DeviceInitializationFailure( - "Failed to retrieve USB devices - return code: '" + std::to_string(libUsbStatusCode) + "'" - ); - } - - while ((device = devices[i++]) != nullptr) { - struct libusb_device_descriptor desc = {}; - - if ((libUsbStatusCode = libusb_get_device_descriptor(device, &desc)) < 0) { - Logger::warning("Failed to retrieve USB device descriptor - return code: '" - + std::to_string(libUsbStatusCode) + "'"); - continue; + if (devices.empty()) { + throw DeviceInitializationFailure("Failed to find USB device with matching vendor & product ID."); } - if (desc.idVendor == vendorIdToMatch && desc.idProduct == productIdToMatch) { - matchedDevices.push_back(device); + if (devices.size() > 1) { + // TODO: implement support for multiple devices (maybe via serial number?) + throw DeviceInitializationFailure( + "Numerous devices of matching vendor & product ID found.\n" + "Please ensure that only one debug tool is connected and then try again." + ); + } + + // For now, just use the first device found. + auto* device = devices.front(); + this->setLibUsbDevice(device); + + const int libUsbStatusCode = libusb_open(libUsbDevice, &this->libUsbDeviceHandle); + + // Obtain a device handle from libusb + if (libUsbStatusCode < 0) { + throw DeviceInitializationFailure( + "Failed to open USB device - error code " + std::to_string(libUsbStatusCode) + " returned." + ); } } - libusb_free_device_list(devices, 1); - return matchedDevices; -} + void UsbDevice::setConfiguration(int configIndex) { + libusb_config_descriptor* configDescriptor = {}; + int libUsbStatusCode = libusb_get_config_descriptor(this->libUsbDevice, 0, &configDescriptor); -void UsbDevice::close() { - if (this->libUsbDeviceHandle != nullptr) { - libusb_close(this->libUsbDeviceHandle); - this->libUsbDeviceHandle = nullptr; + if (libUsbStatusCode < 0) { + throw DeviceInitializationFailure( + "Failed to obtain USB configuration descriptor - error code " + std::to_string(libUsbStatusCode) + + " returned." + ); + } + + libUsbStatusCode = libusb_set_configuration(this->libUsbDeviceHandle, configDescriptor->bConfigurationValue); + if (libUsbStatusCode < 0) { + throw DeviceInitializationFailure( + "Failed to set USB configuration - error code " + std::to_string(libUsbStatusCode) + " returned." + ); + } + + libusb_free_config_descriptor(configDescriptor); } - if (this->libUsbContext != nullptr) { - libusb_exit(this->libUsbContext); + std::vector UsbDevice::findMatchingDevices( + std::optional vendorId, std::optional productId + ) { + auto* libUsbContext = this->libUsbContext; + libusb_device** devices = nullptr; + libusb_device* device; + std::vector matchedDevices; + + auto vendorIdToMatch = vendorId.value_or(this->vendorId); + auto productIdToMatch = productId.value_or(this->productId); + + ssize_t libUsbStatusCode = libusb_get_device_list(libUsbContext, &devices); + if (libUsbStatusCode < 0) { + throw DeviceInitializationFailure( + "Failed to retrieve USB devices - return code: '" + std::to_string(libUsbStatusCode) + "'" + ); + } + + ssize_t i = 0; + while ((device = devices[i++]) != nullptr) { + struct libusb_device_descriptor desc = {}; + + if ((libUsbStatusCode = libusb_get_device_descriptor(device, &desc)) < 0) { + Logger::warning("Failed to retrieve USB device descriptor - return code: '" + + std::to_string(libUsbStatusCode) + "'"); + continue; + } + + if (desc.idVendor == vendorIdToMatch && desc.idProduct == productIdToMatch) { + matchedDevices.push_back(device); + } + } + + libusb_free_device_list(devices, 1); + return matchedDevices; + } + + void UsbDevice::close() { + if (this->libUsbDeviceHandle != nullptr) { + libusb_close(this->libUsbDeviceHandle); + this->libUsbDeviceHandle = nullptr; + } + + if (this->libUsbContext != nullptr) { + libusb_exit(this->libUsbContext); + } } } diff --git a/src/EventManager/EventListener.cpp b/src/EventManager/EventListener.cpp index d2575909..d902f687 100644 --- a/src/EventManager/EventListener.cpp +++ b/src/EventManager/EventListener.cpp @@ -2,98 +2,105 @@ #include "src/Logger/Logger.hpp" -using namespace Bloom; -using namespace Bloom::Events; +namespace Bloom +{ + using namespace Bloom::Events; -std::set EventListener::getRegisteredEventTypes() { - return this->registeredEventTypes.getValue(); -} - -void EventListener::registerEvent(SharedGenericEventPointer event) { - Logger::debug("Event \"" + event->getName() + "\" (" + std::to_string(event->id) - + ") registered for listener " + this->name); - auto queueLock = this->eventQueueByEventType.acquireLock(); - auto& eventQueueByType = this->eventQueueByEventType.getReference(); - - eventQueueByType[event->getType()].push(std::move(event)); - this->eventQueueByEventTypeCV.notify_all(); - - if (this->interruptEventNotifier != nullptr && this->interruptEventNotifier->isInitialised()) { - this->interruptEventNotifier->notify(); + std::set EventListener::getRegisteredEventTypes() { + return this->registeredEventTypes.getValue(); } -} -void EventListener::waitAndDispatch(int msTimeout) { - auto queueLock = this->eventQueueByEventType.acquireLock(); - auto& eventQueueByType = this->eventQueueByEventType.getReference(); - auto registeredEventTypes = this->getRegisteredEventTypes(); - std::optional event; + void EventListener::registerEvent(SharedGenericEventPointer event) { + Logger::debug("Event \"" + event->getName() + "\" (" + std::to_string(event->id) + + ") registered for listener " + this->name); + auto queueLock = this->eventQueueByEventType.acquireLock(); + auto& eventQueueByType = this->eventQueueByEventType.getReference(); + + eventQueueByType[event->getType()].push(std::move(event)); + this->eventQueueByEventTypeCV.notify_all(); + + if (this->interruptEventNotifier != nullptr && this->interruptEventNotifier->isInitialised()) { + this->interruptEventNotifier->notify(); + } + } + + void EventListener::waitAndDispatch(int msTimeout) { + auto queueLock = this->eventQueueByEventType.acquireLock(); + auto& eventQueueByType = this->eventQueueByEventType.getReference(); + auto registeredEventTypes = this->getRegisteredEventTypes(); + std::optional event; + + auto eventsFound = [®isteredEventTypes, &event, &eventQueueByType]() -> bool { + for (auto& eventQueue: eventQueueByType) { + if (registeredEventTypes.contains(eventQueue.first) && !eventQueue.second.empty()) { + return true; + } + } + return false; + }; + + if (msTimeout > 0) { + this->eventQueueByEventTypeCV.wait_for(queueLock, std::chrono::milliseconds(msTimeout), eventsFound); + + } else { + this->eventQueueByEventTypeCV.wait(queueLock, eventsFound); + } + + /* + * We don't want the dispatch to block other threads from registering more events. We don't need the + * lock anymore so it's fine to release it here. + */ + queueLock.unlock(); + + this->dispatchCurrentEvents(); + } + + void EventListener::dispatchEvent(const SharedGenericEventPointer& event) { + Logger::debug("Dispatching event " + event->getName() + " (" + std::to_string(event->id) + ")."); + + // Dispatch the event to all registered handlers + auto mappingLock = this->eventTypeToCallbacksMapping.acquireLock(); + auto& callbacks = this->eventTypeToCallbacksMapping.getReference().find(event->getType())->second; + mappingLock.unlock(); + + for (auto& callback : callbacks) { + callback(*(event.get())); + } + } + + void EventListener::dispatchCurrentEvents() { + auto events = this->getEvents(); + + for (const auto& event: events) { + dispatchEvent(event); + } + } + + std::vector EventListener::getEvents() { + auto queueLock = this->eventQueueByEventType.acquireLock(); + auto& eventQueueByType = this->eventQueueByEventType.getReference(); + std::vector output; - auto eventsFound = [®isteredEventTypes, &event, &eventQueueByType] () -> bool { for (auto& eventQueue: eventQueueByType) { - if (registeredEventTypes.contains(eventQueue.first) && !eventQueue.second.empty()) { - return true; + if (!eventQueue.second.empty()) { + output.push_back(std::move(eventQueue.second.front())); + eventQueue.second.pop(); } } - return false; - }; - if (msTimeout > 0) { - this->eventQueueByEventTypeCV.wait_for(queueLock, std::chrono::milliseconds(msTimeout), eventsFound); + std::sort( + output.begin(), + output.end(), + [](const SharedGenericEventPointer& a, const SharedGenericEventPointer& b) { + return a->id < b->id; + } + ); - } else { - this->eventQueueByEventTypeCV.wait(queueLock, eventsFound); + return output; } - /* - * We don't want the dispatch to block other threads from registering more events. We don't need the - * lock anymore so it's fine to release it here. - */ - queueLock.unlock(); - - this->dispatchCurrentEvents(); -} - -void EventListener::dispatchEvent(const SharedGenericEventPointer& event) { - Logger::debug("Dispatching event " + event->getName() + " (" + std::to_string(event->id) + ")."); - // Dispatch the event to all registered handlers - auto mappingLock = this->eventTypeToCallbacksMapping.acquireLock(); - auto& callbacks = this->eventTypeToCallbacksMapping.getReference().find(event->getType())->second; - mappingLock.unlock(); - - for (auto& callback : callbacks) { - callback(*(event.get())); + void EventListener::clearAllCallbacks() { + auto lock = this->eventTypeToCallbacksMapping.acquireLock(); + this->eventTypeToCallbacksMapping.getReference().clear(); } } - -void EventListener::dispatchCurrentEvents() { - auto events = this->getEvents(); - - for (const auto& event: events) { - dispatchEvent(event); - } -} - -std::vector EventListener::getEvents() { - auto queueLock = this->eventQueueByEventType.acquireLock(); - auto& eventQueueByType = this->eventQueueByEventType.getReference(); - std::vector output; - - for (auto& eventQueue: eventQueueByType) { - if (!eventQueue.second.empty()) { - output.push_back(std::move(eventQueue.second.front())); - eventQueue.second.pop(); - } - } - - std::sort(output.begin(), output.end(), [] (const SharedGenericEventPointer& a, const SharedGenericEventPointer& b) { - return a->id < b->id; - }); - - return output; -} - -void EventListener::clearAllCallbacks() { - auto lock = this->eventTypeToCallbacksMapping.acquireLock(); - this->eventTypeToCallbacksMapping.getReference().clear(); -} diff --git a/src/EventManager/EventManager.cpp b/src/EventManager/EventManager.cpp index 161297da..808e19ba 100644 --- a/src/EventManager/EventManager.cpp +++ b/src/EventManager/EventManager.cpp @@ -1,35 +1,36 @@ #include "EventManager.hpp" -using namespace Bloom; - -void EventManager::registerListener(std::shared_ptr listener) { - auto registerListenersLock = std::unique_lock(this->registerListenerMutex); - this->registeredListeners.insert(std::pair(listener->getId(), std::move(listener))); -} - -void EventManager::deregisterListener(size_t listenerId) { - auto registerListenersLock = std::unique_lock(this->registerListenerMutex); - this->registeredListeners.erase(listenerId); -} - -void EventManager::triggerEvent(const std::shared_ptr& event) { - auto registerListenersLock = std::unique_lock(this->registerListenerMutex); - - for(const auto& [listenerId, listener] : this->registeredListeners) { - if (listener->isEventTypeRegistered(event->getType())) { - listener->registerEvent(event); - } +namespace Bloom +{ + void EventManager::registerListener(std::shared_ptr listener) { + auto registerListenersLock = std::unique_lock(this->registerListenerMutex); + this->registeredListeners.insert(std::pair(listener->getId(), std::move(listener))); } -} -bool EventManager::isEventTypeListenedFor(Events::EventType eventType) { - auto registerListenersLock = std::unique_lock(this->registerListenerMutex); + void EventManager::deregisterListener(size_t listenerId) { + auto registerListenersLock = std::unique_lock(this->registerListenerMutex); + this->registeredListeners.erase(listenerId); + } - for(const auto& [listenerId, listener] : this->registeredListeners) { - if (listener->isEventTypeRegistered(eventType)) { - return true; + void EventManager::triggerEvent(const std::shared_ptr& event) { + auto registerListenersLock = std::unique_lock(this->registerListenerMutex); + + for (const auto&[listenerId, listener] : this->registeredListeners) { + if (listener->isEventTypeRegistered(event->getType())) { + listener->registerEvent(event); + } } } - return false; + bool EventManager::isEventTypeListenedFor(Events::EventType eventType) { + auto registerListenersLock = std::unique_lock(this->registerListenerMutex); + + for (const auto&[listenerId, listener] : this->registeredListeners) { + if (listener->isEventTypeRegistered(eventType)) { + return true; + } + } + + return false; + } } diff --git a/src/Helpers/Paths.cpp b/src/Helpers/Paths.cpp index 9baf138a..bb648b4e 100644 --- a/src/Helpers/Paths.cpp +++ b/src/Helpers/Paths.cpp @@ -7,14 +7,15 @@ #include "src/Exceptions/Exception.hpp" -using namespace Bloom; +namespace Bloom +{ + std::string Paths::applicationDirPath() { + auto pathCharArray = std::array(); -std::string Paths::applicationDirPath() { - auto pathCharArray = std::array(); + if (readlink("/proc/self/exe", pathCharArray.data(), PATH_MAX) < 0) { + throw Exceptions::Exception("Failed to obtain application directory path."); + } - if (readlink("/proc/self/exe", pathCharArray.data(), PATH_MAX) < 0) { - throw Exceptions::Exception("Failed to obtain application directory path."); + return std::filesystem::path(std::string(pathCharArray.begin(), pathCharArray.end())).parent_path(); } - - return std::filesystem::path(std::string(pathCharArray.begin(), pathCharArray.end())).parent_path(); } diff --git a/src/Insight/Insight.cpp b/src/Insight/Insight.cpp index 29cd07f4..bd98fee1 100644 --- a/src/Insight/Insight.cpp +++ b/src/Insight/Insight.cpp @@ -10,203 +10,205 @@ #include "src/Application.hpp" #include "InsightWorker/Tasks/QueryLatestVersionNumber.hpp" -using namespace Bloom; -using namespace Bloom::Exceptions; +namespace Bloom +{ + using namespace Bloom::Exceptions; -void Insight::run() { - try { - this->startup(); + void Insight::run() { + try { + this->startup(); - this->workerThread->start(); - this->setThreadState(ThreadState::READY); - Logger::info("Insight ready"); - this->application.exec(); + this->workerThread->start(); + this->setThreadState(ThreadState::READY); + Logger::info("Insight ready"); + this->application.exec(); - } catch (const Exception& exception) { - Logger::error("Insight encountered a fatal error. See below for errors:"); - Logger::error(exception.getMessage()); + } catch (const Exception& exception) { + Logger::error("Insight encountered a fatal error. See below for errors:"); + Logger::error(exception.getMessage()); - } catch (const std::exception& exception) { - Logger::error("Insight encountered a fatal error. See below for errors:"); - Logger::error(std::string(exception.what())); - } - - this->shutdown(); -} - -void Insight::startup() { - Logger::info("Starting Insight"); - this->setThreadState(ThreadState::STARTING); - this->eventManager.registerListener(this->eventListener); - - this->eventListener->registerCallbackForEventType( - std::bind(&Insight::onShutdownApplicationEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&Insight::onTargetControllerThreadStateChangedEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&Insight::onDebugServerThreadStateChangedEvent, this, std::placeholders::_1) - ); - - auto targetDescriptor = this->targetControllerConsole.getTargetDescriptor(); - - QApplication::setQuitOnLastWindowClosed(true); - QApplication::setStyle(new BloomProxyStyle()); - - auto globalStylesheet = QFile( - QString::fromStdString( - Paths::compiledResourcesPath() + "/src/Insight/UserInterfaces/InsightWindow/Stylesheets/Global.qss" - ) - ); - - if (!globalStylesheet.open(QFile::ReadOnly)) { - throw Exception("Failed to open global stylesheet file"); - } - - this->application.setStyleSheet(globalStylesheet.readAll()); - - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType>(); - - // Load Ubuntu fonts - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-B.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-BI.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-C.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-L.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-LI.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-M.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-MI.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-B.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-BI.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-R.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-RI.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-R.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-RI.ttf") - ); - QFontDatabase::addApplicationFont( - QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-Th.ttf") - ); - - /* - * We can't run our own event loop here - we have to use Qt's event loop. But we still need to be able to - * process our events. To address this, we use a QTimer to dispatch our events on an interval. - * - * This allows us to use Qt's event loop whilst still being able to process our own events. - */ - auto* eventDispatchTimer = new QTimer(&(this->application)); - QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Insight::dispatchEvents); - eventDispatchTimer->start(100); - - this->mainWindow->setInsightConfig(this->insightConfig); - this->mainWindow->setEnvironmentConfig(this->environmentConfig); - - this->mainWindow->init(targetDescriptor); - - // Prepare worker thread - this->workerThread = new QThread(); - this->workerThread->setObjectName("IW"); - this->insightWorker->moveToThread(this->workerThread); - QObject::connect(this->workerThread, &QThread::started, this->insightWorker, &InsightWorker::startup); - QObject::connect(this->workerThread, &QThread::finished, this->insightWorker, &QObject::deleteLater); - QObject::connect(this->workerThread, &QThread::finished, this->workerThread, &QThread::deleteLater); - - QObject::connect(this->insightWorker, &InsightWorker::ready, this, [this] { - this->checkBloomVersion(); - }); - - this->mainWindow->show(); -} - -void Insight::shutdown() { - if (this->getThreadState() == ThreadState::STOPPED) { - return; - } - - Logger::info("Shutting down Insight"); - this->mainWindow->close(); - - if (this->workerThread != nullptr && this->workerThread->isRunning()) { - this->workerThread->quit(); - } - - this->application.exit(0); - - this->setThreadState(ThreadState::STOPPED); -} - -void Insight::checkBloomVersion() { - auto currentVersionNumber = Application::VERSION; - - auto* versionQueryTask = new QueryLatestVersionNumber( - currentVersionNumber - ); - - QObject::connect( - versionQueryTask, - &QueryLatestVersionNumber::latestVersionNumberRetrieved, - this, - [this, currentVersionNumber] (const VersionNumber& latestVersionNumber) { - if (latestVersionNumber > currentVersionNumber) { - Logger::warning( - "Bloom v" + latestVersionNumber.toString() - + " is available to download - upgrade via " + Paths::homeDomainName() - ); - } + } catch (const std::exception& exception) { + Logger::error("Insight encountered a fatal error. See below for errors:"); + Logger::error(std::string(exception.what())); } - ); - this->insightWorker->queueTask(versionQueryTask); -} - -void Insight::onShutdownApplicationEvent(const Events::ShutdownApplication&) { - /* - * Once Insight shuts down, control of the main thread will be returned to Application::run(), which - * will pickup the ShutdownApplication event and proceed with the shutdown. - */ - this->shutdown(); -} - -void Insight::onTargetControllerThreadStateChangedEvent(const Events::TargetControllerThreadStateChanged& event) { - if (event.getState() == ThreadState::STOPPED) { - // Something horrible has happened with the TargetController - Insight is useless without the TargetController this->shutdown(); } -} -void Insight::onDebugServerThreadStateChangedEvent(const Events::DebugServerThreadStateChanged& event) { - if (event.getState() == ThreadState::STOPPED) { - // Something horrible has happened with the DebugServer + void Insight::startup() { + Logger::info("Starting Insight"); + this->setThreadState(ThreadState::STARTING); + this->eventManager.registerListener(this->eventListener); + + this->eventListener->registerCallbackForEventType( + std::bind(&Insight::onShutdownApplicationEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&Insight::onTargetControllerThreadStateChangedEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&Insight::onDebugServerThreadStateChangedEvent, this, std::placeholders::_1) + ); + + auto targetDescriptor = this->targetControllerConsole.getTargetDescriptor(); + + QApplication::setQuitOnLastWindowClosed(true); + QApplication::setStyle(new BloomProxyStyle()); + + auto globalStylesheet = QFile( + QString::fromStdString( + Paths::compiledResourcesPath() + "/src/Insight/UserInterfaces/InsightWindow/Stylesheets/Global.qss" + ) + ); + + if (!globalStylesheet.open(QFile::ReadOnly)) { + throw Exception("Failed to open global stylesheet file"); + } + + this->application.setStyleSheet(globalStylesheet.readAll()); + + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType>(); + + // Load Ubuntu fonts + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-B.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-BI.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-C.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-L.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-LI.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-M.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-MI.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-B.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-BI.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-R.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-RI.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-R.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-RI.ttf") + ); + QFontDatabase::addApplicationFont( + QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-Th.ttf") + ); + + /* + * We can't run our own event loop here - we have to use Qt's event loop. But we still need to be able to + * process our events. To address this, we use a QTimer to dispatch our events on an interval. + * + * This allows us to use Qt's event loop whilst still being able to process our own events. + */ + auto* eventDispatchTimer = new QTimer(&(this->application)); + QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Insight::dispatchEvents); + eventDispatchTimer->start(100); + + this->mainWindow->setInsightConfig(this->insightConfig); + this->mainWindow->setEnvironmentConfig(this->environmentConfig); + + this->mainWindow->init(targetDescriptor); + + // Prepare worker thread + this->workerThread = new QThread(); + this->workerThread->setObjectName("IW"); + this->insightWorker->moveToThread(this->workerThread); + QObject::connect(this->workerThread, &QThread::started, this->insightWorker, &InsightWorker::startup); + QObject::connect(this->workerThread, &QThread::finished, this->insightWorker, &QObject::deleteLater); + QObject::connect(this->workerThread, &QThread::finished, this->workerThread, &QThread::deleteLater); + + QObject::connect(this->insightWorker, &InsightWorker::ready, this, [this] { + this->checkBloomVersion(); + }); + + this->mainWindow->show(); + } + + void Insight::shutdown() { + if (this->getThreadState() == ThreadState::STOPPED) { + return; + } + + Logger::info("Shutting down Insight"); + this->mainWindow->close(); + + if (this->workerThread != nullptr && this->workerThread->isRunning()) { + this->workerThread->quit(); + } + + this->application.exit(0); + + this->setThreadState(ThreadState::STOPPED); + } + + void Insight::checkBloomVersion() { + auto currentVersionNumber = Application::VERSION; + + auto* versionQueryTask = new QueryLatestVersionNumber( + currentVersionNumber + ); + + QObject::connect( + versionQueryTask, + &QueryLatestVersionNumber::latestVersionNumberRetrieved, + this, + [this, currentVersionNumber] (const VersionNumber& latestVersionNumber) { + if (latestVersionNumber > currentVersionNumber) { + Logger::warning( + "Bloom v" + latestVersionNumber.toString() + + " is available to download - upgrade via " + Paths::homeDomainName() + ); + } + } + ); + + this->insightWorker->queueTask(versionQueryTask); + } + + void Insight::onShutdownApplicationEvent(const Events::ShutdownApplication&) { + /* + * Once Insight shuts down, control of the main thread will be returned to Application::run(), which + * will pickup the ShutdownApplication event and proceed with the shutdown. + */ this->shutdown(); } + + void Insight::onTargetControllerThreadStateChangedEvent(const Events::TargetControllerThreadStateChanged& event) { + if (event.getState() == ThreadState::STOPPED) { + // Something horrible has happened with the TargetController - Insight is useless without the TargetController + this->shutdown(); + } + } + + void Insight::onDebugServerThreadStateChangedEvent(const Events::DebugServerThreadStateChanged& event) { + if (event.getState() == ThreadState::STOPPED) { + // Something horrible has happened with the DebugServer + this->shutdown(); + } + } } diff --git a/src/Insight/InsightWorker/InsightWorker.cpp b/src/Insight/InsightWorker/InsightWorker.cpp index 23ffa8cb..645525c4 100644 --- a/src/Insight/InsightWorker/InsightWorker.cpp +++ b/src/Insight/InsightWorker/InsightWorker.cpp @@ -6,135 +6,138 @@ #include "src/Helpers/Thread.hpp" #include "src/Logger/Logger.hpp" -using namespace Bloom; -using namespace Bloom::Exceptions; +namespace Bloom +{ + using namespace Bloom::Exceptions; -using Bloom::Targets::TargetState; + using Bloom::Targets::TargetState; -InsightWorker::InsightWorker(EventManager& eventManager): eventManager(eventManager) {} + InsightWorker::InsightWorker(EventManager& eventManager): eventManager(eventManager) {} -void InsightWorker::queueTask(InsightWorkerTask* task) { - auto taskQueueLock = this->queuedTasks.acquireLock(); - task->moveToThread(this->thread()); - task->setParent(this); - this->queuedTasks.getReference().push(task); - emit this->taskQueued(); -} - -void InsightWorker::startup() { - Logger::debug("Starting InsightWorker thread"); - this->eventManager.registerListener(this->eventListener); - - this->eventListener->registerCallbackForEventType( - std::bind(&InsightWorker::onTargetControllerStateReportedEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&InsightWorker::onTargetStoppedEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&InsightWorker::onTargetResumedEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&InsightWorker::onTargetRegistersWrittenEvent, this, std::placeholders::_1) - ); - - this->eventDispatchTimer = new QTimer(this); - QObject::connect(this->eventDispatchTimer, &QTimer::timeout, this, &InsightWorker::dispatchEvents); - this->eventDispatchTimer->start(5); - - QObject::connect(this, &InsightWorker::taskQueued, this, &InsightWorker::executeTasks); - - this->eventManager.triggerEvent( - std::make_shared(ThreadState::READY) - ); - - emit this->ready(); -} - -void InsightWorker::requestPinStates(int variantId) { - this->targetControllerConsole.requestPinStates(variantId); -} - -std::optional InsightWorker::getQueuedTask() { - auto task = std::optional(); - - auto& queuedTasks = this->queuedTasks.getReference(); - auto taskQueueLock = this->queuedTasks.acquireLock(); - - if (!queuedTasks.empty()) { - task = queuedTasks.front(); - queuedTasks.pop(); + void InsightWorker::queueTask(InsightWorkerTask* task) { + auto taskQueueLock = this->queuedTasks.acquireLock(); + task->moveToThread(this->thread()); + task->setParent(this); + this->queuedTasks.getReference().push(task); + emit this->taskQueued(); } - return task; -} + void InsightWorker::startup() { + Logger::debug("Starting InsightWorker thread"); + this->eventManager.registerListener(this->eventListener); -void InsightWorker::onTargetStoppedEvent(const Events::TargetExecutionStopped& event) { - /* - * When we report a target halt to Insight, Insight will immediately seek more data from the target (such as GPIO - * pin states). This can be problematic for cases where the target had halted due to a conditional breakpoint. - * - * For conditional breakpoints, a software breakpoint is employed to halt target execution and give the debug - * client an opportunity to check the condition. In cases where the condition is not met, the client will - * immediately request for execution to be resumed. It's important that Insight does not get in the way of this - * process, as it could end up slowing things down significantly. - * - * For the above reason, we don't want to report any target halts to Insight, unless we can be sure that the client - * isn't going to immediately resume execution upon checking the condition. - * - * We do this by providing a time window for the TargetExecutionResumed event. If the event is triggered within - * that time window, we won't report the target halt to Insight, thus preventing Insight from needlessly seeking - * data from the target and slowing things down. - * - * This isn't the best approach, TBH, as it introduces a delay to Insight's response to the target halting. The - * problem is, we cannot differentiate a conditional breakpoint with a software breakpoint, so this seems to be the - * only way. It would be nice if the debug client gave us some form of indication of whether the breakpoint is a - * conditional one. - */ - auto resumedEvent = this->eventListener->waitForEvent( - std::chrono::milliseconds(650) - ); + this->eventListener->registerCallbackForEventType( + std::bind(&InsightWorker::onTargetControllerStateReportedEvent, this, std::placeholders::_1) + ); - if (!resumedEvent.has_value()) { - emit this->targetStateUpdated(TargetState::STOPPED); - emit this->targetProgramCounterUpdated(event.programCounter); + this->eventListener->registerCallbackForEventType( + std::bind(&InsightWorker::onTargetStoppedEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&InsightWorker::onTargetResumedEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&InsightWorker::onTargetRegistersWrittenEvent, this, std::placeholders::_1) + ); + + this->eventDispatchTimer = new QTimer(this); + QObject::connect(this->eventDispatchTimer, &QTimer::timeout, this, &InsightWorker::dispatchEvents); + this->eventDispatchTimer->start(5); + + QObject::connect(this, &InsightWorker::taskQueued, this, &InsightWorker::executeTasks); + + this->eventManager.triggerEvent( + std::make_shared(ThreadState::READY) + ); + + emit this->ready(); } -} -void InsightWorker::onTargetResumedEvent(const Events::TargetExecutionResumed& event) { - emit this->targetStateUpdated(TargetState::RUNNING); -} + void InsightWorker::requestPinStates(int variantId) { + this->targetControllerConsole.requestPinStates(variantId); + } -void InsightWorker::onTargetRegistersWrittenEvent(const Events::RegistersWrittenToTarget& event) { - emit this->targetRegistersWritten(event.registers, event.createdTimestamp); -} + std::optional InsightWorker::getQueuedTask() { + auto task = std::optional(); -void InsightWorker::onTargetControllerStateReportedEvent(const Events::TargetControllerStateReported& event) { - if (this->lastTargetControllerState == TargetControllerState::ACTIVE - && event.state == TargetControllerState::SUSPENDED - ) { - emit this->targetControllerSuspended(); + auto& queuedTasks = this->queuedTasks.getReference(); + auto taskQueueLock = this->queuedTasks.acquireLock(); - } else if (this->lastTargetControllerState == TargetControllerState::SUSPENDED - && event.state == TargetControllerState::ACTIVE - ) { - try { - emit this->targetControllerResumed(this->targetControllerConsole.getTargetDescriptor()); + if (!queuedTasks.empty()) { + task = queuedTasks.front(); + queuedTasks.pop(); + } - } catch (const Exception& exception) { - Logger::error("Insight resume failed - " + exception.getMessage()); + return task; + } + + void InsightWorker::onTargetStoppedEvent(const Events::TargetExecutionStopped& event) { + /* + * When we report a target halt to Insight, Insight will immediately seek more data from the target (such as + * GPIO pin states). This can be problematic for cases where the target had halted due to a conditional + * breakpoint. + * + * For conditional breakpoints, a software breakpoint is employed to halt target execution and give the debug + * client an opportunity to check the condition. In cases where the condition is not met, the client will + * immediately request for execution to be resumed. It's important that Insight does not get in the way of this + * process, as it could end up slowing things down significantly. + * + * For the above reason, we don't want to report any target halts to Insight, unless we can be sure that the + * client isn't going to immediately resume execution upon checking the condition. + * + * We do this by providing a time window for the TargetExecutionResumed event. If the event is triggered within + * that time window, we won't report the target halt to Insight, thus preventing Insight from needlessly + * seeking data from the target and slowing things down. + * + * This isn't the best approach, TBH, as it introduces a delay to Insight's response to the target halting. The + * problem is, we cannot differentiate a conditional breakpoint with a software breakpoint, so this seems to be + * the only way. It would be nice if the debug client gave us some form of indication of whether the breakpoint + * is a conditional one. + */ + auto resumedEvent = this->eventListener->waitForEvent( + std::chrono::milliseconds(650) + ); + + if (!resumedEvent.has_value()) { + emit this->targetStateUpdated(TargetState::STOPPED); + emit this->targetProgramCounterUpdated(event.programCounter); } } - this->lastTargetControllerState = event.state; -} -void InsightWorker::executeTasks() { - auto task = std::optional(); + void InsightWorker::onTargetResumedEvent(const Events::TargetExecutionResumed& event) { + emit this->targetStateUpdated(TargetState::RUNNING); + } - while ((task = this->getQueuedTask()).has_value()) { - task.value()->execute(this->targetControllerConsole); + void InsightWorker::onTargetRegistersWrittenEvent(const Events::RegistersWrittenToTarget& event) { + emit this->targetRegistersWritten(event.registers, event.createdTimestamp); + } + + void InsightWorker::onTargetControllerStateReportedEvent(const Events::TargetControllerStateReported& event) { + if (this->lastTargetControllerState == TargetControllerState::ACTIVE + && event.state == TargetControllerState::SUSPENDED + ) { + emit this->targetControllerSuspended(); + + } else if (this->lastTargetControllerState == TargetControllerState::SUSPENDED + && event.state == TargetControllerState::ACTIVE + ) { + try { + emit this->targetControllerResumed(this->targetControllerConsole.getTargetDescriptor()); + + } catch (const Exception& exception) { + Logger::error("Insight resume failed - " + exception.getMessage()); + } + } + this->lastTargetControllerState = event.state; + } + + void InsightWorker::executeTasks() { + auto task = std::optional(); + + while ((task = this->getQueuedTask()).has_value()) { + task.value()->execute(this->targetControllerConsole); + } } } diff --git a/src/Insight/InsightWorker/Tasks/InsightWorkerTask.cpp b/src/Insight/InsightWorker/Tasks/InsightWorkerTask.cpp index 310ee870..65dba6ad 100644 --- a/src/Insight/InsightWorker/Tasks/InsightWorkerTask.cpp +++ b/src/Insight/InsightWorker/Tasks/InsightWorkerTask.cpp @@ -2,19 +2,20 @@ #include "src/Logger/Logger.hpp" -using namespace Bloom; +namespace Bloom +{ + void InsightWorkerTask::execute(TargetControllerConsole& targetControllerConsole) { + try { + this->state = InsightWorkerTaskState::STARTED; + emit this->started(); + this->run(targetControllerConsole); + this->state = InsightWorkerTaskState::COMPLETED; + emit this->completed(); -void InsightWorkerTask::execute(TargetControllerConsole& targetControllerConsole) { - try { - this->state = InsightWorkerTaskState::STARTED; - emit this->started(); - this->run(targetControllerConsole); - this->state = InsightWorkerTaskState::COMPLETED; - emit this->completed(); - - } catch (std::exception& exception) { - this->state = InsightWorkerTaskState::FAILED; - Logger::error("InsightWorker task failed - " + std::string(exception.what())); - emit this->failed(QString::fromStdString(exception.what())); + } catch (std::exception& exception) { + this->state = InsightWorkerTaskState::FAILED; + Logger::error("InsightWorker task failed - " + std::string(exception.what())); + emit this->failed(QString::fromStdString(exception.what())); + } } } diff --git a/src/Insight/InsightWorker/Tasks/QueryLatestVersionNumber.cpp b/src/Insight/InsightWorker/Tasks/QueryLatestVersionNumber.cpp index c524aa9b..2b3950a2 100644 --- a/src/Insight/InsightWorker/Tasks/QueryLatestVersionNumber.cpp +++ b/src/Insight/InsightWorker/Tasks/QueryLatestVersionNumber.cpp @@ -9,26 +9,27 @@ #include "src/Helpers/Paths.hpp" -using namespace Bloom; +namespace Bloom +{ + void QueryLatestVersionNumber::run(TargetControllerConsole& targetControllerConsole) { + auto* networkAccessManager = new QNetworkAccessManager(this); + auto queryVersionEndpointUrl = QUrl(QString::fromStdString(Paths::homeDomainName() + "/latest-version")); + queryVersionEndpointUrl.setScheme("http"); + queryVersionEndpointUrl.setQuery(QUrlQuery({ + {"currentVersionNumber", QString::fromStdString(this->currentVersionNumber.toString())} + })); -void QueryLatestVersionNumber::run(TargetControllerConsole& targetControllerConsole) { - auto* networkAccessManager = new QNetworkAccessManager(this); - auto queryVersionEndpointUrl = QUrl(QString::fromStdString(Paths::homeDomainName() + "/latest-version")); - queryVersionEndpointUrl.setScheme("http"); - queryVersionEndpointUrl.setQuery(QUrlQuery({ - {"currentVersionNumber", QString::fromStdString(this->currentVersionNumber.toString())} - })); + auto* response = networkAccessManager->get(QNetworkRequest(queryVersionEndpointUrl)); + QObject::connect(response, &QNetworkReply::finished, this, [this, response] { + const auto jsonResponseObject = QJsonDocument::fromJson(response->readAll()).object(); - auto* response = networkAccessManager->get(QNetworkRequest(queryVersionEndpointUrl)); - QObject::connect(response, &QNetworkReply::finished, this, [this, response] { - const auto jsonResponseObject = QJsonDocument::fromJson(response->readAll()).object(); - - if (jsonResponseObject.contains("latestVersionNumber")) { - emit this->latestVersionNumberRetrieved( - VersionNumber( - jsonResponseObject.value("latestVersionNumber").toString().toStdString() - ) - ); - } - }); + if (jsonResponseObject.contains("latestVersionNumber")) { + emit this->latestVersionNumberRetrieved( + VersionNumber( + jsonResponseObject.value("latestVersionNumber").toString().toStdString() + ) + ); + } + }); + } } diff --git a/src/Insight/InsightWorker/Tasks/ReadStackPointer.cpp b/src/Insight/InsightWorker/Tasks/ReadStackPointer.cpp index e99543cc..bbe483f2 100644 --- a/src/Insight/InsightWorker/Tasks/ReadStackPointer.cpp +++ b/src/Insight/InsightWorker/Tasks/ReadStackPointer.cpp @@ -1,7 +1,8 @@ #include "ReadStackPointer.hpp" -using namespace Bloom; - -void ReadStackPointer::run(TargetControllerConsole& targetControllerConsole) { - emit this->stackPointerRead(targetControllerConsole.getStackPointer()); +namespace Bloom +{ + void ReadStackPointer::run(TargetControllerConsole& targetControllerConsole) { + emit this->stackPointerRead(targetControllerConsole.getStackPointer()); + } } diff --git a/src/Insight/InsightWorker/Tasks/ReadTargetMemory.cpp b/src/Insight/InsightWorker/Tasks/ReadTargetMemory.cpp index af9fae23..7c0c5d17 100644 --- a/src/Insight/InsightWorker/Tasks/ReadTargetMemory.cpp +++ b/src/Insight/InsightWorker/Tasks/ReadTargetMemory.cpp @@ -1,14 +1,15 @@ #include "ReadTargetMemory.hpp" -using namespace Bloom; - -void ReadTargetMemory::run(TargetControllerConsole& targetControllerConsole) { - emit this->targetMemoryRead( - targetControllerConsole.readMemory( - this->memoryType, - this->startAddress, - this->size, - this->excludedAddressRanges - ) - ); +namespace Bloom +{ + void ReadTargetMemory::run(TargetControllerConsole& targetControllerConsole) { + emit this->targetMemoryRead( + targetControllerConsole.readMemory( + this->memoryType, + this->startAddress, + this->size, + this->excludedAddressRanges + ) + ); + } } diff --git a/src/Insight/InsightWorker/Tasks/ReadTargetRegisters.cpp b/src/Insight/InsightWorker/Tasks/ReadTargetRegisters.cpp index bf3a8572..eb92a8cf 100644 --- a/src/Insight/InsightWorker/Tasks/ReadTargetRegisters.cpp +++ b/src/Insight/InsightWorker/Tasks/ReadTargetRegisters.cpp @@ -1,7 +1,8 @@ #include "ReadTargetRegisters.hpp" -using namespace Bloom; - -void ReadTargetRegisters::run(TargetControllerConsole& targetControllerConsole) { - emit this->targetRegistersRead(targetControllerConsole.readRegisters(this->descriptors)); +namespace Bloom +{ + void ReadTargetRegisters::run(TargetControllerConsole& targetControllerConsole) { + emit this->targetRegistersRead(targetControllerConsole.readRegisters(this->descriptors)); + } } diff --git a/src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.cpp b/src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.cpp index 18b6b5a9..c6030e33 100644 --- a/src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.cpp +++ b/src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.cpp @@ -1,7 +1,8 @@ #include "RefreshTargetPinStates.hpp" -using namespace Bloom; - -void RefreshTargetPinStates::run(TargetControllerConsole& targetControllerConsole) { - emit this->targetPinStatesRetrieved(targetControllerConsole.getPinStates(this->variantId)); +namespace Bloom +{ + void RefreshTargetPinStates::run(TargetControllerConsole& targetControllerConsole) { + emit this->targetPinStatesRetrieved(targetControllerConsole.getPinStates(this->variantId)); + } } diff --git a/src/Insight/InsightWorker/Tasks/SetTargetPinState.cpp b/src/Insight/InsightWorker/Tasks/SetTargetPinState.cpp index d53d452b..ae3f17c0 100644 --- a/src/Insight/InsightWorker/Tasks/SetTargetPinState.cpp +++ b/src/Insight/InsightWorker/Tasks/SetTargetPinState.cpp @@ -1,7 +1,8 @@ #include "SetTargetPinState.hpp" -using namespace Bloom; - -void SetTargetPinState::run(TargetControllerConsole& targetControllerConsole) { - targetControllerConsole.setPinState(this->pinDescriptor, this->pinState); +namespace Bloom +{ + void SetTargetPinState::run(TargetControllerConsole& targetControllerConsole) { + targetControllerConsole.setPinState(this->pinDescriptor, this->pinState); + } } diff --git a/src/Insight/InsightWorker/Tasks/WriteTargetRegister.cpp b/src/Insight/InsightWorker/Tasks/WriteTargetRegister.cpp index 70fbcf4e..ca7a2d42 100644 --- a/src/Insight/InsightWorker/Tasks/WriteTargetRegister.cpp +++ b/src/Insight/InsightWorker/Tasks/WriteTargetRegister.cpp @@ -1,7 +1,8 @@ #include "WriteTargetRegister.hpp" -using namespace Bloom; - -void WriteTargetRegister::run(TargetControllerConsole& targetControllerConsole) { - targetControllerConsole.writeRegisters({this->targetRegister}); +namespace Bloom +{ + void WriteTargetRegister::run(TargetControllerConsole& targetControllerConsole) { + targetControllerConsole.writeRegisters({this->targetRegister}); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/AboutWindow.cpp b/src/Insight/UserInterfaces/InsightWindow/AboutWindow.cpp index 204c6a5d..2beb4c9f 100644 --- a/src/Insight/UserInterfaces/InsightWindow/AboutWindow.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/AboutWindow.cpp @@ -6,37 +6,39 @@ #include "src/Helpers/Paths.hpp" #include "src/Application.hpp" -using namespace Bloom; -using namespace Exceptions; +namespace Bloom +{ + using namespace Exceptions; -AboutWindow::AboutWindow(QWidget* parent): QObject(parent) { - auto aboutWindowUiFile = QFile(QString::fromStdString( - Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/UiFiles/AboutWindow.ui" - ) - ); - auto aboutWindowStylesheet = QFile(QString::fromStdString( - Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Stylesheets/AboutWindow.qss" - ) - ); + AboutWindow::AboutWindow(QWidget* parent): QObject(parent) { + auto aboutWindowUiFile = QFile(QString::fromStdString( + Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/UiFiles/AboutWindow.ui" + ) + ); + auto aboutWindowStylesheet = QFile(QString::fromStdString( + Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Stylesheets/AboutWindow.qss" + ) + ); - if (!aboutWindowUiFile.open(QFile::ReadOnly)) { - throw Exception("Failed to open AboutWindow UI file"); - } + if (!aboutWindowUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open AboutWindow UI file"); + } - if (!aboutWindowStylesheet.open(QFile::ReadOnly)) { - throw Exception("Failed to open AboutWindow QSS file"); - } + if (!aboutWindowStylesheet.open(QFile::ReadOnly)) { + throw Exception("Failed to open AboutWindow QSS file"); + } - auto uiLoader = QUiLoader(); - this->windowWidget = uiLoader.load(&aboutWindowUiFile, parent); - this->windowWidget->setStyleSheet(aboutWindowStylesheet.readAll()); - this->windowWidget->setFixedSize(400, 300); + auto uiLoader = QUiLoader(); + this->windowWidget = uiLoader.load(&aboutWindowUiFile, parent); + this->windowWidget->setStyleSheet(aboutWindowStylesheet.readAll()); + this->windowWidget->setFixedSize(400, 300); - auto versionLabel = this->windowWidget->findChild("version-label"); + auto versionLabel = this->windowWidget->findChild("version-label"); - if (versionLabel != nullptr) { - versionLabel->setText("Bloom v" + QString::fromStdString(Application::VERSION.toString())); + if (versionLabel != nullptr) { + versionLabel->setText("Bloom v" + QString::fromStdString(Application::VERSION.toString())); + } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/BloomProxyStyle.cpp b/src/Insight/UserInterfaces/InsightWindow/BloomProxyStyle.cpp index c3e5923c..71473081 100644 --- a/src/Insight/UserInterfaces/InsightWindow/BloomProxyStyle.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/BloomProxyStyle.cpp @@ -1,16 +1,17 @@ #include "BloomProxyStyle.hpp" -using namespace Bloom; +namespace Bloom +{ + int BloomProxyStyle::styleHint( + StyleHint hint, + const QStyleOption* option, + const QWidget* widget, + QStyleHintReturn* returnData + ) const { + if (hint == QStyle::SH_ComboBox_Popup) { + return 0; + } -int BloomProxyStyle::styleHint( - StyleHint hint, - const QStyleOption* option, - const QWidget* widget, - QStyleHintReturn* returnData -) const { - if (hint == QStyle::SH_ComboBox_Popup) { - return 0; + return QProxyStyle::styleHint(hint, option, widget, returnData); } - - return QProxyStyle::styleHint(hint, option, widget, returnData); } diff --git a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp index 2f2064a1..cda8d049 100644 --- a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp @@ -15,787 +15,791 @@ #include "src/Helpers/Paths.hpp" #include "src/Targets/TargetDescriptor.hpp" -using namespace Bloom; -using namespace Bloom::Exceptions; -using namespace Bloom::Widgets; - -using Bloom::Targets::TargetDescriptor; -using Bloom::Targets::TargetState; -using Bloom::Targets::TargetPinState; -using Bloom::Targets::TargetVariant; -using Bloom::Targets::TargetPackage; -using Bloom::Targets::TargetPinDescriptor; -using Bloom::Targets::TargetMemoryType; - -InsightWindow::InsightWindow( - InsightWorker& insightWorker, - const EnvironmentConfig& environmentConfig, - const InsightConfig& insightConfig, - InsightProjectSettings& insightProjectSettings -): - QMainWindow(nullptr), - insightWorker(insightWorker), - environmentConfig(environmentConfig), - targetConfig(environmentConfig.targetConfig), - insightConfig(insightConfig), - insightProjectSettings(insightProjectSettings) +namespace Bloom { - this->setObjectName("main-window"); - this->setWindowTitle("Bloom Insight"); + using namespace Bloom::Exceptions; + using namespace Bloom::Widgets; - const auto defaultMinimumSize = QSize(1000, 500); + using Bloom::Targets::TargetDescriptor; + using Bloom::Targets::TargetState; + using Bloom::Targets::TargetPinState; + using Bloom::Targets::TargetVariant; + using Bloom::Targets::TargetPackage; + using Bloom::Targets::TargetPinDescriptor; + using Bloom::Targets::TargetMemoryType; - if (this->insightProjectSettings.mainWindowSize.has_value()) { - this->setMinimumSize( - std::max(this->insightProjectSettings.mainWindowSize->width(), defaultMinimumSize.width()), - std::max(this->insightProjectSettings.mainWindowSize->height(), defaultMinimumSize.height()) - ); + InsightWindow::InsightWindow( + InsightWorker& insightWorker, + const EnvironmentConfig& environmentConfig, + const InsightConfig& insightConfig, + InsightProjectSettings& insightProjectSettings + ) + : QMainWindow(nullptr) + , insightWorker(insightWorker) + , environmentConfig(environmentConfig) + , targetConfig(environmentConfig.targetConfig) + , insightConfig(insightConfig) + , insightProjectSettings(insightProjectSettings) + { + this->setObjectName("main-window"); + this->setWindowTitle("Bloom Insight"); - } else { - this->setMinimumSize(defaultMinimumSize); - } + const auto defaultMinimumSize = QSize(1000, 500); - auto mainWindowUiFile = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui" - ) - ); - auto mainWindowStylesheet = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss" - ) - ); - - if (!mainWindowUiFile.open(QFile::ReadOnly)) { - throw Exception("Failed to open InsightWindow UI file"); - } - - if (!mainWindowStylesheet.open(QFile::ReadOnly)) { - throw Exception("Failed to open InsightWindow stylesheet file"); - } - - auto uiLoader = UiLoader(this); - this->windowContainer = uiLoader.load(&mainWindowUiFile, this); - this->windowContainer->setStyleSheet(mainWindowStylesheet.readAll()); - - mainWindowUiFile.close(); - mainWindowStylesheet.close(); - - QApplication::setWindowIcon(QIcon( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Images/BloomIcon.svg" - ) - )); - - this->layoutContainer = this->windowContainer->findChild("layout-container"); - this->mainMenuBar = this->windowContainer->findChild("menu-bar"); - this->layoutContainer->layout()->setMenuBar(this->mainMenuBar); - this->container = this->layoutContainer->findChild("container"); - this->ioContainerWidget = this->windowContainer->findChild( - "io-container" - ); - this->ioUnavailableWidget = this->windowContainer->findChild("io-inspection-unavailable"); - - auto* fileMenu = this->mainMenuBar->findChild("file-menu"); - auto* helpMenu = this->mainMenuBar->findChild("help-menu"); - auto* quitAction = fileMenu->findChild("close-insight"); - auto* openReportIssuesUrlAction = helpMenu->findChild("open-report-issues-url"); - auto* openGettingStartedUrlAction = helpMenu->findChild("open-getting-started-url"); - auto* openAboutWindowAction = helpMenu->findChild("open-about-dialogue"); - - this->header = this->windowContainer->findChild("header"); - this->refreshIoInspectionButton = this->header->findChild("refresh-io-inspection-btn"); - - this->leftMenuBar = this->container->findChild("left-side-menu-bar"); - this->leftPanel = this->container->findChild("left-panel"); - - this->targetRegistersButton = this->container->findChild("target-registers-btn"); - auto* targetRegisterButtonLayout = this->targetRegistersButton->findChild(); - auto* registersBtnLabel = new RotatableLabel(270, "Registers", this->targetRegistersButton); - registersBtnLabel->setObjectName("target-registers-btn-label"); - registersBtnLabel->setContentsMargins(5,0,9,0); - targetRegisterButtonLayout->insertWidget(0, registersBtnLabel, 0, Qt::AlignTop); - - this->bottomMenuBar = this->container->findChild("bottom-menu-bar"); - this->bottomPanel = this->container->findChild("bottom-panel"); - - this->ramInspectionButton = this->container->findChild("ram-inspection-btn"); - this->eepromInspectionButton = this->container->findChild("eeprom-inspection-btn"); - - this->footer = this->windowContainer->findChild("footer"); - this->targetStatusLabel = this->footer->findChild("target-state"); - this->programCounterValueLabel = this->footer->findChild("target-program-counter-value"); - this->targetNameLabel = this->footer->findChild("target-name"); - this->targetIdLabel = this->footer->findChild("target-id"); - this->variantMenu = this->footer->findChild("target-variant-menu"); - - const auto windowSize = this->size(); - this->windowContainer->setFixedSize(windowSize); - this->layoutContainer->setFixedSize(windowSize); - - // Main menu connections - QObject::connect( - quitAction, - &QAction::triggered, - this, - &InsightWindow::close - ); - QObject::connect( - openReportIssuesUrlAction, - &QAction::triggered, - this, - &InsightWindow::openReportIssuesUrl - ); - QObject::connect( - openGettingStartedUrlAction, - &QAction::triggered, - this, - &InsightWindow::openGettingStartedUrl - ); - QObject::connect( - openAboutWindowAction, - &QAction::triggered, - this, - &InsightWindow::openAboutWindow - ); - - // Tool bar button connections - QObject::connect( - this->refreshIoInspectionButton, - &QToolButton::clicked, - this, - &InsightWindow::refresh - ); - - // Panel button connections - QObject::connect( - this->targetRegistersButton, - &QToolButton::clicked, - this, - &InsightWindow::toggleTargetRegistersPane - ); - QObject::connect( - this->ramInspectionButton, - &QToolButton::clicked, - this, - &InsightWindow::toggleRamInspectionPane - ); - QObject::connect( - this->eepromInspectionButton, - &QToolButton::clicked, - this, - &InsightWindow::toggleEepromInspectionPane - ); - - // InsightWorker connections - QObject::connect( - &(this->insightWorker), - &InsightWorker::targetControllerSuspended, - this, - &InsightWindow::onTargetControllerSuspended - ); - QObject::connect( - &(this->insightWorker), - &InsightWorker::targetControllerResumed, - this, - &InsightWindow::onTargetControllerResumed - ); - QObject::connect( - &(this->insightWorker), - &InsightWorker::targetStateUpdated, - this, - &InsightWindow::onTargetStateUpdate - ); - QObject::connect( - &(this->insightWorker), - &InsightWorker::targetProgramCounterUpdated, - this, - &InsightWindow::onTargetProgramCounterUpdate - ); -} - -void InsightWindow::init(TargetDescriptor targetDescriptor) { - this->targetDescriptor = std::move(targetDescriptor); - this->activate(); -} - -void InsightWindow::resizeEvent(QResizeEvent* event) { - const auto windowSize = this->size(); - - this->windowContainer->setFixedSize(windowSize); - this->layoutContainer->setFixedSize(windowSize); - - this->adjustPanels(); -} - -void InsightWindow::showEvent(QShowEvent* event) { - this->adjustPanels(); - this->adjustMinimumSize(); -} - -void InsightWindow::closeEvent(QCloseEvent* event) { - this->insightProjectSettings.mainWindowSize = this->size(); - - return QMainWindow::closeEvent(event); -} - -bool InsightWindow::isVariantSupported(const TargetVariant& variant) { - const auto pinCount = variant.pinDescriptorsByNumber.size(); - - /* - * Because the size of the pin body widget is fixed, for all of our target package widgets, we run out of screen - * estate for target variants with more than 100 pins. - * - * This will be addressed at some point, but for now, we just won't support variants with more than 100 pins. - */ - if (pinCount > 100) { - return false; - } - - if (variant.package == TargetPackage::DIP - || variant.package == TargetPackage::SOIC - || variant.package == TargetPackage::SSOP - ) { - // All DIP, SOIC and SSOP variants must have a pin count that is a multiple of two - if (pinCount % 2 == 0) { - return true; - } - } - - if (variant.package == TargetPackage::QFP || variant.package == TargetPackage::QFN) { - /* - * All QFP and QFN variants must have a pin count that is a multiple of four. And there must be - * more than one pin per side. - */ - if (pinCount % 4 == 0 && pinCount > 4) { - return true; - } - } - - return false; -} - -void InsightWindow::setUiDisabled(bool disable) { - this->uiDisabled = disable; - - if (this->refreshIoInspectionButton != nullptr) { - this->refreshIoInspectionButton->setDisabled(disable); - this->refreshIoInspectionButton->repaint(); - } -} - -void InsightWindow::activate() { - this->targetNameLabel->setText(QString::fromStdString(this->targetDescriptor.name)); - this->targetIdLabel->setText("0x" + QString::fromStdString(this->targetDescriptor.id).remove("0x").toUpper()); - - this->ioUnavailableWidget->hide(); - - this->populateVariantMenu(); - this->variantMenu->setEnabled(true); - - Logger::debug("Number of target variants supported by Insight: " - + std::to_string(supportedVariantsByName.size())); - - if (this->supportedVariantsByName.empty()) { - if (this->targetDescriptor.variants.empty()) { - this->variantMenu->parentWidget()->hide(); - } - - this->ioUnavailableWidget->setText( - "GPIO inspection is not available for this target. " - "Please report this to Bloom developers by clicking Help -> Report An Issue" - ); - this->ioUnavailableWidget->show(); - - } else { - this->selectDefaultVariant(); - } - - this->createPanes(); - - this->setUiDisabled(this->targetState != TargetState::STOPPED); - this->activated = true; -} - -void InsightWindow::populateVariantMenu() { - /* - * We don't want to present the user with duplicate target variants. - * - * In the context of the Insight window, two variants are considered to be duplicates if they do not differ in - * package type and pinout configuration. - */ - auto processedVariants = std::vector(); - const auto isDuplicateVariant = [&processedVariants] (const TargetVariant& variantA) { - return std::ranges::any_of( - processedVariants.begin(), - processedVariants.end(), - [&variantA, &processedVariants] (const TargetVariant& variantB) { - if (variantA.package != variantB.package) { - return false; - } - - if (variantA.pinDescriptorsByNumber.size() != variantB.pinDescriptorsByNumber.size()) { - return false; - } - - if (variantA.pinDescriptorsByNumber != variantB.pinDescriptorsByNumber) { - return false; - } - - return true; - } - ); - }; - - for (const auto& targetVariant: this->targetDescriptor.variants) { - if (isDuplicateVariant(targetVariant)) { - continue; - } - - auto* variantAction = new QAction(this->variantMenu); - variantAction->setText( - QString::fromStdString(targetVariant.name + " (" + targetVariant.packageName + ")") - ); - - if (InsightWindow::isVariantSupported(targetVariant)) { - auto* supportedVariantPtr = &(this->supportedVariantsByName.insert( - std::pair(QString::fromStdString(targetVariant.name).toLower(), targetVariant) - ).first->second); - - QObject::connect( - variantAction, - &QAction::triggered, - this, - [this, supportedVariantPtr] { - this->selectVariant(supportedVariantPtr); - } + if (this->insightProjectSettings.mainWindowSize.has_value()) { + this->setMinimumSize( + std::max(this->insightProjectSettings.mainWindowSize->width(), defaultMinimumSize.width()), + std::max(this->insightProjectSettings.mainWindowSize->height(), defaultMinimumSize.height()) ); } else { - variantAction->setEnabled(false); - variantAction->setText(variantAction->text() + " (unsupported)"); + this->setMinimumSize(defaultMinimumSize); } - this->variantMenu->addAction(variantAction); - processedVariants.push_back(targetVariant); - } -} + auto mainWindowUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui" + ) + ); + auto mainWindowStylesheet = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss" + ) + ); -void InsightWindow::selectDefaultVariant() { - if (this->supportedVariantsByName.empty()) { - return; + if (!mainWindowUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open InsightWindow UI file"); + } + + if (!mainWindowStylesheet.open(QFile::ReadOnly)) { + throw Exception("Failed to open InsightWindow stylesheet file"); + } + + auto uiLoader = UiLoader(this); + this->windowContainer = uiLoader.load(&mainWindowUiFile, this); + this->windowContainer->setStyleSheet(mainWindowStylesheet.readAll()); + + mainWindowUiFile.close(); + mainWindowStylesheet.close(); + + QApplication::setWindowIcon(QIcon( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Images/BloomIcon.svg" + ) + )); + + this->layoutContainer = this->windowContainer->findChild("layout-container"); + this->mainMenuBar = this->windowContainer->findChild("menu-bar"); + this->layoutContainer->layout()->setMenuBar(this->mainMenuBar); + this->container = this->layoutContainer->findChild("container"); + this->ioContainerWidget = this->windowContainer->findChild( + "io-container" + ); + this->ioUnavailableWidget = this->windowContainer->findChild("io-inspection-unavailable"); + + auto* fileMenu = this->mainMenuBar->findChild("file-menu"); + auto* helpMenu = this->mainMenuBar->findChild("help-menu"); + auto* quitAction = fileMenu->findChild("close-insight"); + auto* openReportIssuesUrlAction = helpMenu->findChild("open-report-issues-url"); + auto* openGettingStartedUrlAction = helpMenu->findChild("open-getting-started-url"); + auto* openAboutWindowAction = helpMenu->findChild("open-about-dialogue"); + + this->header = this->windowContainer->findChild("header"); + this->refreshIoInspectionButton = this->header->findChild("refresh-io-inspection-btn"); + + this->leftMenuBar = this->container->findChild("left-side-menu-bar"); + this->leftPanel = this->container->findChild("left-panel"); + + this->targetRegistersButton = this->container->findChild("target-registers-btn"); + auto* targetRegisterButtonLayout = this->targetRegistersButton->findChild(); + auto* registersBtnLabel = new RotatableLabel(270, "Registers", this->targetRegistersButton); + registersBtnLabel->setObjectName("target-registers-btn-label"); + registersBtnLabel->setContentsMargins(5,0,9,0); + targetRegisterButtonLayout->insertWidget(0, registersBtnLabel, 0, Qt::AlignTop); + + this->bottomMenuBar = this->container->findChild("bottom-menu-bar"); + this->bottomPanel = this->container->findChild("bottom-panel"); + + this->ramInspectionButton = this->container->findChild("ram-inspection-btn"); + this->eepromInspectionButton = this->container->findChild("eeprom-inspection-btn"); + + this->footer = this->windowContainer->findChild("footer"); + this->targetStatusLabel = this->footer->findChild("target-state"); + this->programCounterValueLabel = this->footer->findChild("target-program-counter-value"); + this->targetNameLabel = this->footer->findChild("target-name"); + this->targetIdLabel = this->footer->findChild("target-id"); + this->variantMenu = this->footer->findChild("target-variant-menu"); + + const auto windowSize = this->size(); + this->windowContainer->setFixedSize(windowSize); + this->layoutContainer->setFixedSize(windowSize); + + // Main menu connections + QObject::connect( + quitAction, + &QAction::triggered, + this, + &InsightWindow::close + ); + QObject::connect( + openReportIssuesUrlAction, + &QAction::triggered, + this, + &InsightWindow::openReportIssuesUrl + ); + QObject::connect( + openGettingStartedUrlAction, + &QAction::triggered, + this, + &InsightWindow::openGettingStartedUrl + ); + QObject::connect( + openAboutWindowAction, + &QAction::triggered, + this, + &InsightWindow::openAboutWindow + ); + + // Tool bar button connections + QObject::connect( + this->refreshIoInspectionButton, + &QToolButton::clicked, + this, + &InsightWindow::refresh + ); + + // Panel button connections + QObject::connect( + this->targetRegistersButton, + &QToolButton::clicked, + this, + &InsightWindow::toggleTargetRegistersPane + ); + QObject::connect( + this->ramInspectionButton, + &QToolButton::clicked, + this, + &InsightWindow::toggleRamInspectionPane + ); + QObject::connect( + this->eepromInspectionButton, + &QToolButton::clicked, + this, + &InsightWindow::toggleEepromInspectionPane + ); + + // InsightWorker connections + QObject::connect( + &(this->insightWorker), + &InsightWorker::targetControllerSuspended, + this, + &InsightWindow::onTargetControllerSuspended + ); + QObject::connect( + &(this->insightWorker), + &InsightWorker::targetControllerResumed, + this, + &InsightWindow::onTargetControllerResumed + ); + QObject::connect( + &(this->insightWorker), + &InsightWorker::targetStateUpdated, + this, + &InsightWindow::onTargetStateUpdate + ); + QObject::connect( + &(this->insightWorker), + &InsightWorker::targetProgramCounterUpdated, + this, + &InsightWindow::onTargetProgramCounterUpdate + ); } - std::optional previouslySelectedVariantName = (this->previouslySelectedVariant.has_value()) ? - std::optional(QString::fromStdString(this->previouslySelectedVariant->name).toLower()) - : std::nullopt; + void InsightWindow::init(TargetDescriptor targetDescriptor) { + this->targetDescriptor = std::move(targetDescriptor); + this->activate(); + } - if (previouslySelectedVariantName.has_value() - && this->supportedVariantsByName.contains(previouslySelectedVariantName.value()) - ) { - this->selectVariant(&(this->supportedVariantsByName.at(previouslySelectedVariantName.value()))); + void InsightWindow::resizeEvent(QResizeEvent* event) { + const auto windowSize = this->size(); - } else if (this->targetConfig.variantName.has_value()) { - auto selectedVariantName = QString::fromStdString(this->targetConfig.variantName.value()); - if (this->supportedVariantsByName.contains(selectedVariantName)) { - // The user has specified a valid variant name in their config file, so use that as the default - this->selectVariant(&(this->supportedVariantsByName.at(selectedVariantName))); + this->windowContainer->setFixedSize(windowSize); + this->layoutContainer->setFixedSize(windowSize); + + this->adjustPanels(); + } + + void InsightWindow::showEvent(QShowEvent* event) { + this->adjustPanels(); + this->adjustMinimumSize(); + } + + void InsightWindow::closeEvent(QCloseEvent* event) { + this->insightProjectSettings.mainWindowSize = this->size(); + + return QMainWindow::closeEvent(event); + } + + bool InsightWindow::isVariantSupported(const TargetVariant& variant) { + const auto pinCount = variant.pinDescriptorsByNumber.size(); + + /* + * Because the size of the pin body widget is fixed, for all of our target package widgets, we run out of screen + * estate for target variants with more than 100 pins. + * + * This will be addressed at some point, but for now, we just won't support variants with more than 100 pins. + */ + if (pinCount > 100) { + return false; + } + + if (variant.package == TargetPackage::DIP + || variant.package == TargetPackage::SOIC + || variant.package == TargetPackage::SSOP + ) { + // All DIP, SOIC and SSOP variants must have a pin count that is a multiple of two + if (pinCount % 2 == 0) { + return true; + } + } + + if (variant.package == TargetPackage::QFP || variant.package == TargetPackage::QFN) { + /* + * All QFP and QFN variants must have a pin count that is a multiple of four. And there must be + * more than one pin per side. + */ + if (pinCount % 4 == 0 && pinCount > 4) { + return true; + } + } + + return false; + } + + void InsightWindow::setUiDisabled(bool disable) { + this->uiDisabled = disable; + + if (this->refreshIoInspectionButton != nullptr) { + this->refreshIoInspectionButton->setDisabled(disable); + this->refreshIoInspectionButton->repaint(); + } + } + + void InsightWindow::activate() { + this->targetNameLabel->setText(QString::fromStdString(this->targetDescriptor.name)); + this->targetIdLabel->setText("0x" + QString::fromStdString(this->targetDescriptor.id).remove("0x").toUpper()); + + this->ioUnavailableWidget->hide(); + + this->populateVariantMenu(); + this->variantMenu->setEnabled(true); + + Logger::debug("Number of target variants supported by Insight: " + + std::to_string(supportedVariantsByName.size())); + + if (this->supportedVariantsByName.empty()) { + if (this->targetDescriptor.variants.empty()) { + this->variantMenu->parentWidget()->hide(); + } + + this->ioUnavailableWidget->setText( + "GPIO inspection is not available for this target. " + "Please report this to Bloom developers by clicking Help -> Report An Issue" + ); + this->ioUnavailableWidget->show(); } else { - Logger::error("Invalid target variant name \"" + this->targetConfig.variantName.value() - + "\" - no such variant with the given name was found."); + this->selectDefaultVariant(); + } + + this->createPanes(); + + this->setUiDisabled(this->targetState != TargetState::STOPPED); + this->activated = true; + } + + void InsightWindow::populateVariantMenu() { + /* + * We don't want to present the user with duplicate target variants. + * + * In the context of the Insight window, two variants are considered to be duplicates if they do not differ in + * package type and pinout configuration. + */ + auto processedVariants = std::vector(); + const auto isDuplicateVariant = [&processedVariants] (const TargetVariant& variantA) { + return std::ranges::any_of( + processedVariants.begin(), + processedVariants.end(), + [&variantA, &processedVariants] (const TargetVariant& variantB) { + if (variantA.package != variantB.package) { + return false; + } + + if (variantA.pinDescriptorsByNumber.size() != variantB.pinDescriptorsByNumber.size()) { + return false; + } + + if (variantA.pinDescriptorsByNumber != variantB.pinDescriptorsByNumber) { + return false; + } + + return true; + } + ); + }; + + for (const auto& targetVariant: this->targetDescriptor.variants) { + if (isDuplicateVariant(targetVariant)) { + continue; + } + + auto* variantAction = new QAction(this->variantMenu); + variantAction->setText( + QString::fromStdString(targetVariant.name + " (" + targetVariant.packageName + ")") + ); + + if (InsightWindow::isVariantSupported(targetVariant)) { + auto* supportedVariantPtr = &(this->supportedVariantsByName.insert( + std::pair(QString::fromStdString(targetVariant.name).toLower(), targetVariant) + ).first->second); + + QObject::connect( + variantAction, + &QAction::triggered, + this, + [this, supportedVariantPtr] { + this->selectVariant(supportedVariantPtr); + } + ); + + } else { + variantAction->setEnabled(false); + variantAction->setText(variantAction->text() + " (unsupported)"); + } + + this->variantMenu->addAction(variantAction); + processedVariants.push_back(targetVariant); } } - if (this->selectedVariant == nullptr) { + void InsightWindow::selectDefaultVariant() { + if (this->supportedVariantsByName.empty()) { + return; + } + + std::optional previouslySelectedVariantName = (this->previouslySelectedVariant.has_value()) ? + std::optional(QString::fromStdString(this->previouslySelectedVariant->name).toLower()) + : std::nullopt; + + if (previouslySelectedVariantName.has_value() + && this->supportedVariantsByName.contains(previouslySelectedVariantName.value()) + ) { + this->selectVariant(&(this->supportedVariantsByName.at(previouslySelectedVariantName.value()))); + + } else if (this->targetConfig.variantName.has_value()) { + auto selectedVariantName = QString::fromStdString(this->targetConfig.variantName.value()); + if (this->supportedVariantsByName.contains(selectedVariantName)) { + // The user has specified a valid variant name in their config file, so use that as the default + this->selectVariant(&(this->supportedVariantsByName.at(selectedVariantName))); + + } else { + Logger::error("Invalid target variant name \"" + this->targetConfig.variantName.value() + + "\" - no such variant with the given name was found."); + } + } + + if (this->selectedVariant == nullptr) { + /* + * Given that we haven't been able to select a variant at this point, we will just fallback to the first + * one that is available. + */ + this->selectVariant(&(this->supportedVariantsByName.begin()->second)); + } + } + + void InsightWindow::selectVariant(const TargetVariant* variant) { + if (!this->isVariantSupported(*variant)) { + Logger::error("Attempted to select unsupported target variant."); + return; + } + + if (this->selectedVariant != nullptr && this->selectedVariant->id == variant->id) { + // The variant is already selected. + return; + } + + if (this->targetPackageWidget != nullptr) { + this->targetPackageWidget->hide(); + this->targetPackageWidget->deleteLater(); + this->targetPackageWidget = nullptr; + this->ioContainerWidget->setPackageWidget(this->targetPackageWidget); + } + + this->selectedVariant = variant; + this->variantMenu->setTitle(QString::fromStdString(variant->name + " (" + variant->packageName + ")")); + + if (variant->package == TargetPackage::DIP + || variant->package == TargetPackage::SOIC + || variant->package == TargetPackage::SSOP + ) { + this->targetPackageWidget = new InsightTargetWidgets::Dip::DualInlinePackageWidget( + *variant, + this->insightWorker, + this->ioContainerWidget + ); + + } else if (variant->package == TargetPackage::QFP || variant->package == TargetPackage::QFN) { + this->targetPackageWidget = new InsightTargetWidgets::Qfp::QuadFlatPackageWidget( + *variant, + this->insightWorker, + this->ioContainerWidget + ); + } + + if (this->targetPackageWidget != nullptr) { + this->ioContainerWidget->setPackageWidget(this->targetPackageWidget); + this->targetPackageWidget->setTargetState(this->targetState); + + if (this->targetState == TargetState::STOPPED) { + this->targetPackageWidget->refreshPinStates([this] { + if (this->targetState == TargetState::STOPPED) { + this->targetPackageWidget->setDisabled(false); + } + }); + } + + this->adjustPanels(); + this->adjustMinimumSize(); + this->targetPackageWidget->show(); + } + } + + void InsightWindow::createPanes() { + // Target registers pane + auto* leftPanelLayout = this->leftPanel->layout(); + this->targetRegistersSidePane = new TargetRegistersPaneWidget( + this->targetDescriptor, + this->insightWorker, + this->leftPanel + ); + leftPanelLayout->addWidget(this->targetRegistersSidePane); + this->targetRegistersButton->setChecked(false); + this->targetRegistersButton->setDisabled(false); + + auto& memoryInspectionPaneSettingsByMemoryType = + this->insightProjectSettings.memoryInspectionPaneSettingsByMemoryType; + + // Target memory inspection panes + auto* bottomPanelLayout = this->bottomPanel->layout(); + if (this->targetDescriptor.memoryDescriptorsByType.contains(TargetMemoryType::RAM)) { + auto& ramDescriptor = this->targetDescriptor.memoryDescriptorsByType.at(TargetMemoryType::RAM); + + if (!memoryInspectionPaneSettingsByMemoryType.contains(TargetMemoryType::RAM)) { + memoryInspectionPaneSettingsByMemoryType[TargetMemoryType::RAM] = TargetMemoryInspectionPaneSettings(); + } + + this->ramInspectionPane = new TargetMemoryInspectionPane( + ramDescriptor, + memoryInspectionPaneSettingsByMemoryType[TargetMemoryType::RAM], + this->insightWorker, + this->bottomPanel + ); + + bottomPanelLayout->addWidget(this->ramInspectionPane); + this->ramInspectionPane->deactivate(); + this->ramInspectionButton->setChecked(false); + this->ramInspectionButton->setDisabled(false); + } + + if (this->targetDescriptor.memoryDescriptorsByType.contains(TargetMemoryType::EEPROM)) { + auto& eepromDescriptor = this->targetDescriptor.memoryDescriptorsByType.at(TargetMemoryType::EEPROM); + + if (!memoryInspectionPaneSettingsByMemoryType.contains(TargetMemoryType::EEPROM)) { + memoryInspectionPaneSettingsByMemoryType[TargetMemoryType::EEPROM] = TargetMemoryInspectionPaneSettings(); + } + + this->eepromInspectionPane = new TargetMemoryInspectionPane( + eepromDescriptor, + memoryInspectionPaneSettingsByMemoryType[TargetMemoryType::EEPROM], + this->insightWorker, + this->bottomPanel + ); + + bottomPanelLayout->addWidget(this->eepromInspectionPane); + this->eepromInspectionPane->deactivate(); + this->eepromInspectionButton->setChecked(false); + this->eepromInspectionButton->setDisabled(false); + } + } + + void InsightWindow::destroyPanes() { + if (this->targetRegistersSidePane != nullptr) { + this->targetRegistersSidePane->deactivate(); + this->targetRegistersSidePane->deleteLater(); + this->leftPanel->setVisible(false); + this->targetRegistersButton->setChecked(false); + this->targetRegistersButton->setDisabled(true); + } + /* - * Given that we haven't been able to select a variant at this point, we will just fallback to the first - * one that is available. + * Before we destroy the memory inspection pane widgets, we take a copy of their current settings (memory + * regions, hex viewer settings, etc), in order to persist them through debug sessions. */ - this->selectVariant(&(this->supportedVariantsByName.begin()->second)); - } -} + if (this->ramInspectionPane != nullptr) { + this->ramInspectionPane->deactivate(); + this->ramInspectionPane->deleteLater(); + this->ramInspectionPane = nullptr; -void InsightWindow::selectVariant(const TargetVariant* variant) { - if (!this->isVariantSupported(*variant)) { - Logger::error("Attempted to select unsupported target variant."); - return; + this->bottomPanel->setVisible(false); + this->ramInspectionButton->setChecked(false); + this->ramInspectionButton->setDisabled(true); + } + + if (this->eepromInspectionPane != nullptr) { + this->eepromInspectionPane->deactivate(); + this->eepromInspectionPane->deleteLater(); + this->eepromInspectionPane = nullptr; + + this->bottomPanel->setVisible(false); + this->eepromInspectionButton->setChecked(false); + this->eepromInspectionButton->setDisabled(true); + } } - if (this->selectedVariant != nullptr && this->selectedVariant->id == variant->id) { - // The variant is already selected. - return; + void InsightWindow::deactivate() { + if (this->selectedVariant != nullptr) { + this->previouslySelectedVariant = *(this->selectedVariant); + this->selectedVariant = nullptr; + } + + if (this->targetPackageWidget != nullptr) { + this->targetPackageWidget->hide(); + this->targetPackageWidget->deleteLater(); + this->targetPackageWidget = nullptr; + this->ioContainerWidget->setPackageWidget(this->targetPackageWidget); + } + + this->destroyPanes(); + + this->ioUnavailableWidget->setText( + "Insight deactivated - Bloom has been disconnected from the target.\n\n" + "Bloom will attempt to reconnect upon the start of a new debug session." + ); + this->ioUnavailableWidget->show(); + + this->targetStatusLabel->setText("Unknown"); + this->programCounterValueLabel->setText("-"); + + this->variantMenu->clear(); + this->variantMenu->setEnabled(false); + + this->supportedVariantsByName.clear(); + + this->setUiDisabled(true); + this->activated = false; } - if (this->targetPackageWidget != nullptr) { - this->targetPackageWidget->hide(); - this->targetPackageWidget->deleteLater(); - this->targetPackageWidget = nullptr; - this->ioContainerWidget->setPackageWidget(this->targetPackageWidget); - } + void InsightWindow::adjustPanels() { + const auto targetPackageWidgetSize = (this->targetPackageWidget != nullptr) + ? this->targetPackageWidget->size() : QSize(); + const auto containerSize = this->container->size(); - this->selectedVariant = variant; - this->variantMenu->setTitle(QString::fromStdString(variant->name + " (" + variant->packageName + ")")); - - if (variant->package == TargetPackage::DIP - || variant->package == TargetPackage::SOIC - || variant->package == TargetPackage::SSOP - ) { - this->targetPackageWidget = new InsightTargetWidgets::Dip::DualInlinePackageWidget( - *variant, - this->insightWorker, - this->ioContainerWidget + /* + * The purpose of the -20 is to ensure there is some padding between the panel borders and the + * target package widget. Looks nicer with the padding. + */ + this->leftPanel->setMaximumResize( + std::max( + this->leftPanel->getMinimumResize(), + containerSize.width() - targetPackageWidgetSize.width() - this->leftMenuBar->width() - 20 + ) ); - } else if (variant->package == TargetPackage::QFP || variant->package == TargetPackage::QFN) { - this->targetPackageWidget = new InsightTargetWidgets::Qfp::QuadFlatPackageWidget( - *variant, - this->insightWorker, - this->ioContainerWidget + /* + * Allow the bottom panel to overlap the target package widget (because the target package widget can + * occupy a lot of space and become an annoyance if the bottom panel is restricted too much). + */ + this->bottomPanel->setMaximumResize( + std::max( + this->bottomPanel->getMinimumResize(), + (containerSize.height() / 2) - this->bottomMenuBar->height() + ) ); } - if (this->targetPackageWidget != nullptr) { - this->ioContainerWidget->setPackageWidget(this->targetPackageWidget); - this->targetPackageWidget->setTargetState(this->targetState); + void InsightWindow::adjustMinimumSize() { + auto minSize = QSize(800, 500); - if (this->targetState == TargetState::STOPPED) { + if (this->targetPackageWidget != nullptr) { + minSize.setWidth(this->targetPackageWidget->width() + 700); + minSize.setHeight(this->targetPackageWidget->height() + 150); + } + + if (this->bottomPanel->isVisible()) { + minSize.setHeight(minSize.height() + this->bottomPanel->getMinimumResize()); + } + + this->setMinimumSize(minSize); + } + + void InsightWindow::onTargetControllerSuspended() { + if (this->activated) { + this->deactivate(); + } + } + + void InsightWindow::onTargetControllerResumed(const TargetDescriptor& targetDescriptor) { + if (!this->activated) { + this->targetDescriptor = targetDescriptor; + this->activate(); + } + } + + void InsightWindow::onTargetStateUpdate(TargetState newState) { + this->targetState = newState; + + if (newState == TargetState::RUNNING) { + this->targetStatusLabel->setText("Running"); + this->programCounterValueLabel->setText("-"); + this->setUiDisabled(true); + + } else if (newState == TargetState::STOPPED) { + this->targetStatusLabel->setText("Stopped"); + this->setUiDisabled(false); + + } else { + this->targetStatusLabel->setText("Unknown"); + } + } + + void InsightWindow::onTargetProgramCounterUpdate(quint32 programCounter) { + this->programCounterValueLabel->setText( + "0x" + QString::number(programCounter, 16).toUpper() + " (" + QString::number(programCounter) + ")" + ); + } + + void InsightWindow::refresh() { + if (this->targetState != TargetState::STOPPED || this->selectedVariant == nullptr) { + return; + } + + this->setUiDisabled(true); + this->refreshIoInspectionButton->startSpin(); + + if (this->targetPackageWidget != nullptr) { + this->targetPackageWidget->setDisabled(true); this->targetPackageWidget->refreshPinStates([this] { if (this->targetState == TargetState::STOPPED) { this->targetPackageWidget->setDisabled(false); + + if (this->targetRegistersSidePane == nullptr || !this->targetRegistersSidePane->activated) { + this->refreshIoInspectionButton->stopSpin(); + this->setUiDisabled(false); + } } }); } - this->adjustPanels(); - this->adjustMinimumSize(); - this->targetPackageWidget->show(); - } -} - -void InsightWindow::createPanes() { - // Target registers pane - auto* leftPanelLayout = this->leftPanel->layout(); - this->targetRegistersSidePane = new TargetRegistersPaneWidget( - this->targetDescriptor, - this->insightWorker, - this->leftPanel - ); - leftPanelLayout->addWidget(this->targetRegistersSidePane); - this->targetRegistersButton->setChecked(false); - this->targetRegistersButton->setDisabled(false); - - auto& memoryInspectionPaneSettingsByMemoryType = - this->insightProjectSettings.memoryInspectionPaneSettingsByMemoryType; - - // Target memory inspection panes - auto* bottomPanelLayout = this->bottomPanel->layout(); - if (this->targetDescriptor.memoryDescriptorsByType.contains(TargetMemoryType::RAM)) { - auto& ramDescriptor = this->targetDescriptor.memoryDescriptorsByType.at(TargetMemoryType::RAM); - - if (!memoryInspectionPaneSettingsByMemoryType.contains(TargetMemoryType::RAM)) { - memoryInspectionPaneSettingsByMemoryType[TargetMemoryType::RAM] = TargetMemoryInspectionPaneSettings(); + if (this->targetRegistersSidePane != nullptr && this->targetRegistersSidePane->activated) { + this->targetRegistersSidePane->refreshRegisterValues([this] { + this->refreshIoInspectionButton->stopSpin(); + this->setUiDisabled(false); + }); } - - this->ramInspectionPane = new TargetMemoryInspectionPane( - ramDescriptor, - memoryInspectionPaneSettingsByMemoryType[TargetMemoryType::RAM], - this->insightWorker, - this->bottomPanel - ); - - bottomPanelLayout->addWidget(this->ramInspectionPane); - this->ramInspectionPane->deactivate(); - this->ramInspectionButton->setChecked(false); - this->ramInspectionButton->setDisabled(false); } - if (this->targetDescriptor.memoryDescriptorsByType.contains(TargetMemoryType::EEPROM)) { - auto& eepromDescriptor = this->targetDescriptor.memoryDescriptorsByType.at(TargetMemoryType::EEPROM); - - if (!memoryInspectionPaneSettingsByMemoryType.contains(TargetMemoryType::EEPROM)) { - memoryInspectionPaneSettingsByMemoryType[TargetMemoryType::EEPROM] = TargetMemoryInspectionPaneSettings(); - } - - this->eepromInspectionPane = new TargetMemoryInspectionPane( - eepromDescriptor, - memoryInspectionPaneSettingsByMemoryType[TargetMemoryType::EEPROM], - this->insightWorker, - this->bottomPanel - ); - - bottomPanelLayout->addWidget(this->eepromInspectionPane); - this->eepromInspectionPane->deactivate(); - this->eepromInspectionButton->setChecked(false); - this->eepromInspectionButton->setDisabled(false); - } -} - -void InsightWindow::destroyPanes() { - if (this->targetRegistersSidePane != nullptr) { - this->targetRegistersSidePane->deactivate(); - this->targetRegistersSidePane->deleteLater(); - this->leftPanel->setVisible(false); - this->targetRegistersButton->setChecked(false); - this->targetRegistersButton->setDisabled(true); - } - - /* - * Before we destroy the memory inspection pane widgets, we take a copy of their current settings (memory regions, - * hex viewer settings, etc), in order to persist them through debug sessions. - */ - if (this->ramInspectionPane != nullptr) { - this->ramInspectionPane->deactivate(); - this->ramInspectionPane->deleteLater(); - this->ramInspectionPane = nullptr; - - this->bottomPanel->setVisible(false); - this->ramInspectionButton->setChecked(false); - this->ramInspectionButton->setDisabled(true); - } - - if (this->eepromInspectionPane != nullptr) { - this->eepromInspectionPane->deactivate(); - this->eepromInspectionPane->deleteLater(); - this->eepromInspectionPane = nullptr; - - this->bottomPanel->setVisible(false); - this->eepromInspectionButton->setChecked(false); - this->eepromInspectionButton->setDisabled(true); - } -} - -void InsightWindow::deactivate() { - if (this->selectedVariant != nullptr) { - this->previouslySelectedVariant = *(this->selectedVariant); - this->selectedVariant = nullptr; - } - - if (this->targetPackageWidget != nullptr) { - this->targetPackageWidget->hide(); - this->targetPackageWidget->deleteLater(); - this->targetPackageWidget = nullptr; - this->ioContainerWidget->setPackageWidget(this->targetPackageWidget); - } - - this->destroyPanes(); - - this->ioUnavailableWidget->setText( - "Insight deactivated - Bloom has been disconnected from the target.\n\n" - "Bloom will attempt to reconnect upon the start of a new debug session." - ); - this->ioUnavailableWidget->show(); - - this->targetStatusLabel->setText("Unknown"); - this->programCounterValueLabel->setText("-"); - - this->variantMenu->clear(); - this->variantMenu->setEnabled(false); - - this->supportedVariantsByName.clear(); - - this->setUiDisabled(true); - this->activated = false; -} - -void InsightWindow::adjustPanels() { - const auto targetPackageWidgetSize = (this->targetPackageWidget != nullptr) - ? this->targetPackageWidget->size() : QSize(); - const auto containerSize = this->container->size(); - - /* - * The purpose of the -20 is to ensure there is some padding between the panel borders and the - * target package widget. Looks nicer with the padding. - */ - this->leftPanel->setMaximumResize( - std::max( - this->leftPanel->getMinimumResize(), - containerSize.width() - targetPackageWidgetSize.width() - this->leftMenuBar->width() - 20 - ) - ); - - /* - * Allow the bottom panel to overlap the target package widget (because the target package widget can - * occupy a lot of space and become an annoyance if the bottom panel is restricted too much). - */ - this->bottomPanel->setMaximumResize( - std::max( - this->bottomPanel->getMinimumResize(), - (containerSize.height() / 2) - this->bottomMenuBar->height() - ) - ); -} - -void InsightWindow::adjustMinimumSize() { - auto minSize = QSize(800, 500); - - if (this->targetPackageWidget != nullptr) { - minSize.setWidth(this->targetPackageWidget->width() + 700); - minSize.setHeight(this->targetPackageWidget->height() + 150); - } - - if (this->bottomPanel->isVisible()) { - minSize.setHeight(minSize.height() + this->bottomPanel->getMinimumResize()); - } - - this->setMinimumSize(minSize); -} - -void InsightWindow::onTargetControllerSuspended() { - if (this->activated) { - this->deactivate(); - } -} - -void InsightWindow::onTargetControllerResumed(const TargetDescriptor& targetDescriptor) { - if (!this->activated) { - this->targetDescriptor = targetDescriptor; - this->activate(); - } -} - -void InsightWindow::onTargetStateUpdate(TargetState newState) { - this->targetState = newState; - - if (newState == TargetState::RUNNING) { - this->targetStatusLabel->setText("Running"); - this->programCounterValueLabel->setText("-"); - this->setUiDisabled(true); - - } else if (newState == TargetState::STOPPED) { - this->targetStatusLabel->setText("Stopped"); - this->setUiDisabled(false); - - } else { - this->targetStatusLabel->setText("Unknown"); - } -} - -void InsightWindow::onTargetProgramCounterUpdate(quint32 programCounter) { - this->programCounterValueLabel->setText( - "0x" + QString::number(programCounter, 16).toUpper() + " (" + QString::number(programCounter) + ")" - ); -} - -void InsightWindow::refresh() { - if (this->targetState != TargetState::STOPPED || this->selectedVariant == nullptr) { - return; - } - - this->setUiDisabled(true); - this->refreshIoInspectionButton->startSpin(); - - if (this->targetPackageWidget != nullptr) { - this->targetPackageWidget->setDisabled(true); - this->targetPackageWidget->refreshPinStates([this] { - if (this->targetState == TargetState::STOPPED) { - this->targetPackageWidget->setDisabled(false); - - if (this->targetRegistersSidePane == nullptr || !this->targetRegistersSidePane->activated) { - this->refreshIoInspectionButton->stopSpin(); - this->setUiDisabled(false); - } - } - }); - } - - if (this->targetRegistersSidePane != nullptr && this->targetRegistersSidePane->activated) { - this->targetRegistersSidePane->refreshRegisterValues([this] { - this->refreshIoInspectionButton->stopSpin(); - this->setUiDisabled(false); - }); - } -} - -void InsightWindow::openReportIssuesUrl() { - auto url = QUrl(QString::fromStdString(Paths::homeDomainName() + "/report-issue")); - /* - * The https://bloom.oscillate.io/report-issue URL just redirects to the Bloom GitHub issue page. - * - * We can use query parameters in the URL to pre-fill the body of the issue. We use this to include some - * target information. - */ - auto urlQuery = QUrlQuery(); - auto issueBody = QString("Issue reported via Bloom Insight.\nTarget name: " - + QString::fromStdString(this->targetDescriptor.name) + "\n" - + "Target ID: " + QString::fromStdString(this->targetDescriptor.id) + "\n" - ); - - if (this->selectedVariant != nullptr) { - issueBody += "Target variant: " + QString::fromStdString(this->selectedVariant->name) + "\n"; - } - - issueBody += "\nPlease describe your issue below. Include as much detail as possible."; - urlQuery.addQueryItem("body", issueBody); - url.setQuery(urlQuery); - - QDesktopServices::openUrl(url); -} - -void InsightWindow::openGettingStartedUrl() { - QDesktopServices::openUrl(QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/getting-started"))); -} - -void InsightWindow::openAboutWindow() { - if (this->aboutWindowWidget == nullptr) { - this->aboutWindowWidget = new AboutWindow(this); - } - - this->aboutWindowWidget->show(); -} - -void InsightWindow::toggleTargetRegistersPane() { - if (this->targetRegistersSidePane->activated) { - this->targetRegistersSidePane->deactivate(); - this->targetRegistersButton->setChecked(false); - + void InsightWindow::openReportIssuesUrl() { + auto url = QUrl(QString::fromStdString(Paths::homeDomainName() + "/report-issue")); /* - * Given that the target registers side pane is currently the only pane in the left panel, the panel will be - * empty so no need to leave it visible. + * The https://bloom.oscillate.io/report-issue URL just redirects to the Bloom GitHub issue page. + * + * We can use query parameters in the URL to pre-fill the body of the issue. We use this to include some + * target information. */ - this->leftPanel->setVisible(false); + auto urlQuery = QUrlQuery(); + auto issueBody = QString("Issue reported via Bloom Insight.\nTarget name: " + + QString::fromStdString(this->targetDescriptor.name) + "\n" + + "Target ID: " + QString::fromStdString(this->targetDescriptor.id) + "\n" + ); - } else { - this->targetRegistersSidePane->activate(); - this->targetRegistersButton->setChecked(true); - this->leftPanel->setVisible(true); - } -} - -void InsightWindow::toggleRamInspectionPane() { - if (this->ramInspectionPane->activated) { - this->ramInspectionPane->deactivate(); - this->bottomPanel->hide(); - this->ramInspectionButton->setChecked(false); - - } else { - if (this->eepromInspectionPane != nullptr && this->eepromInspectionPane->activated) { - this->toggleEepromInspectionPane(); + if (this->selectedVariant != nullptr) { + issueBody += "Target variant: " + QString::fromStdString(this->selectedVariant->name) + "\n"; } - this->ramInspectionPane->activate(); - this->bottomPanel->show(); - this->ramInspectionButton->setChecked(true); + issueBody += "\nPlease describe your issue below. Include as much detail as possible."; + urlQuery.addQueryItem("body", issueBody); + url.setQuery(urlQuery); + + QDesktopServices::openUrl(url); } - this->adjustMinimumSize(); -} + void InsightWindow::openGettingStartedUrl() { + QDesktopServices::openUrl( + QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/getting-started")) + ); + } -void InsightWindow::toggleEepromInspectionPane() { - if (this->eepromInspectionPane->activated) { - this->eepromInspectionPane->deactivate(); - this->bottomPanel->hide(); - this->eepromInspectionButton->setChecked(false); - - } else { - if (this->ramInspectionPane != nullptr && this->ramInspectionPane->activated) { - this->toggleRamInspectionPane(); + void InsightWindow::openAboutWindow() { + if (this->aboutWindowWidget == nullptr) { + this->aboutWindowWidget = new AboutWindow(this); } - this->eepromInspectionPane->activate(); - this->bottomPanel->show(); - this->eepromInspectionButton->setChecked(true); + this->aboutWindowWidget->show(); } - this->adjustMinimumSize(); + void InsightWindow::toggleTargetRegistersPane() { + if (this->targetRegistersSidePane->activated) { + this->targetRegistersSidePane->deactivate(); + this->targetRegistersButton->setChecked(false); + + /* + * Given that the target registers side pane is currently the only pane in the left panel, the panel + * will be empty so no need to leave it visible. + */ + this->leftPanel->setVisible(false); + + } else { + this->targetRegistersSidePane->activate(); + this->targetRegistersButton->setChecked(true); + this->leftPanel->setVisible(true); + } + } + + void InsightWindow::toggleRamInspectionPane() { + if (this->ramInspectionPane->activated) { + this->ramInspectionPane->deactivate(); + this->bottomPanel->hide(); + this->ramInspectionButton->setChecked(false); + + } else { + if (this->eepromInspectionPane != nullptr && this->eepromInspectionPane->activated) { + this->toggleEepromInspectionPane(); + } + + this->ramInspectionPane->activate(); + this->bottomPanel->show(); + this->ramInspectionButton->setChecked(true); + } + + this->adjustMinimumSize(); + } + + void InsightWindow::toggleEepromInspectionPane() { + if (this->eepromInspectionPane->activated) { + this->eepromInspectionPane->deactivate(); + this->bottomPanel->hide(); + this->eepromInspectionButton->setChecked(false); + + } else { + if (this->ramInspectionPane != nullptr && this->ramInspectionPane->activated) { + this->toggleRamInspectionPane(); + } + + this->eepromInspectionPane->activate(); + this->bottomPanel->show(); + this->eepromInspectionButton->setChecked(true); + } + + this->adjustMinimumSize(); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/UiLoader.cpp b/src/Insight/UserInterfaces/InsightWindow/UiLoader.cpp index 5e0b6b09..e8fb350f 100644 --- a/src/Insight/UserInterfaces/InsightWindow/UiLoader.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/UiLoader.cpp @@ -12,91 +12,93 @@ #include "Widgets/ExpandingHeightScrollAreaWidget.hpp" #include "Widgets/TargetWidgets/TargetPackageWidgetContainer.hpp" -using namespace Bloom; -using namespace Bloom::Widgets; +namespace Bloom +{ + using namespace Bloom::Widgets; -UiLoader::UiLoader(QObject* parent): QUiLoader(parent) { - this->customWidgetConstructorsByWidgetName = decltype(this->customWidgetConstructorsByWidgetName) { - { - "PanelWidget", - [this] (QWidget* parent, const QString& name) { - auto* widget = new PanelWidget(parent); - widget->setObjectName(name); - widget->setStyleSheet(parent->styleSheet()); - return widget; - } - }, - { - "RotatableLabel", - [this] (QWidget* parent, const QString& name) { - auto* widget = new RotatableLabel("", parent); - widget->setObjectName(name); - widget->setStyleSheet(parent->styleSheet()); - return widget; - } - }, - { - "LabeledSeparator", - [this] (QWidget* parent, const QString& name) { - auto* widget = new LabeledSeparator(parent); - widget->setObjectName(name); - widget->setStyleSheet(parent->styleSheet()); - return widget; - } - }, - { - "TextInput", - [this] (QWidget* parent, const QString& name) { - auto* widget = new TextInput(parent); - widget->setObjectName(name); - widget->setStyleSheet(parent->styleSheet()); - return widget; - } - }, - { - "ExpandingHeightScrollAreaWidget", - [this] (QWidget* parent, const QString& name) { - auto* widget = new ExpandingHeightScrollAreaWidget(parent); - widget->setObjectName(name); - widget->setStyleSheet(parent->styleSheet()); - return widget; - } - }, - { - "SvgWidget", - [this] (QWidget* parent, const QString& name) { - auto* widget = new SvgWidget(parent); - widget->setObjectName(name); - widget->setStyleSheet(parent->styleSheet()); - return widget; - } - }, - { - "SvgToolButton", - [this] (QWidget* parent, const QString& name) { - auto* widget = new SvgToolButton(parent); - widget->setObjectName(name); - widget->setStyleSheet(parent->styleSheet()); - return widget; - } - }, - { - "TargetPackageWidgetContainer", - [this] (QWidget* parent, const QString& name) { - auto* widget = new InsightTargetWidgets::TargetPackageWidgetContainer(parent); - widget->setObjectName(name); - widget->setStyleSheet(parent->styleSheet()); - return widget; - } - }, - }; -} - -QWidget* UiLoader::createWidget(const QString& className, QWidget* parent, const QString& name) { - if (this->customWidgetConstructorsByWidgetName.contains(className)) { - // This is a custom widget - call the mapped constructor - return this->customWidgetConstructorsByWidgetName.at(className)(parent, name); + UiLoader::UiLoader(QObject* parent): QUiLoader(parent) { + this->customWidgetConstructorsByWidgetName = decltype(this->customWidgetConstructorsByWidgetName) { + { + "PanelWidget", + [this] (QWidget* parent, const QString& name) { + auto* widget = new PanelWidget(parent); + widget->setObjectName(name); + widget->setStyleSheet(parent->styleSheet()); + return widget; + } + }, + { + "RotatableLabel", + [this] (QWidget* parent, const QString& name) { + auto* widget = new RotatableLabel("", parent); + widget->setObjectName(name); + widget->setStyleSheet(parent->styleSheet()); + return widget; + } + }, + { + "LabeledSeparator", + [this] (QWidget* parent, const QString& name) { + auto* widget = new LabeledSeparator(parent); + widget->setObjectName(name); + widget->setStyleSheet(parent->styleSheet()); + return widget; + } + }, + { + "TextInput", + [this] (QWidget* parent, const QString& name) { + auto* widget = new TextInput(parent); + widget->setObjectName(name); + widget->setStyleSheet(parent->styleSheet()); + return widget; + } + }, + { + "ExpandingHeightScrollAreaWidget", + [this] (QWidget* parent, const QString& name) { + auto* widget = new ExpandingHeightScrollAreaWidget(parent); + widget->setObjectName(name); + widget->setStyleSheet(parent->styleSheet()); + return widget; + } + }, + { + "SvgWidget", + [this] (QWidget* parent, const QString& name) { + auto* widget = new SvgWidget(parent); + widget->setObjectName(name); + widget->setStyleSheet(parent->styleSheet()); + return widget; + } + }, + { + "SvgToolButton", + [this] (QWidget* parent, const QString& name) { + auto* widget = new SvgToolButton(parent); + widget->setObjectName(name); + widget->setStyleSheet(parent->styleSheet()); + return widget; + } + }, + { + "TargetPackageWidgetContainer", + [this] (QWidget* parent, const QString& name) { + auto* widget = new InsightTargetWidgets::TargetPackageWidgetContainer(parent); + widget->setObjectName(name); + widget->setStyleSheet(parent->styleSheet()); + return widget; + } + }, + }; } - return QUiLoader::createWidget(className, parent, name); + QWidget* UiLoader::createWidget(const QString& className, QWidget* parent, const QString& name) { + if (this->customWidgetConstructorsByWidgetName.contains(className)) { + // This is a custom widget - call the mapped constructor + return this->customWidgetConstructorsByWidgetName.at(className)(parent, name); + } + + return QUiLoader::createWidget(className, parent, name); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.cpp index e4f64c7a..43be5e14 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.cpp @@ -1,22 +1,23 @@ #include "ClickableWidget.hpp" -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + void ClickableWidget::mouseReleaseEvent(QMouseEvent* event) { + if (event->button() == Qt::MouseButton::LeftButton) { + emit this->clicked(); -void ClickableWidget::mouseReleaseEvent(QMouseEvent* event) { - if (event->button() == Qt::MouseButton::LeftButton) { - emit this->clicked(); + } else if (event->button() == Qt::MouseButton::RightButton) { + emit this->rightClicked(); + } - } else if (event->button() == Qt::MouseButton::RightButton) { - emit this->rightClicked(); + QWidget::mouseReleaseEvent(event); } - QWidget::mouseReleaseEvent(event); -} + void ClickableWidget::mouseDoubleClickEvent(QMouseEvent* event) { + if (event->button() == Qt::MouseButton::LeftButton) { + emit this->doubleClicked(); + } -void ClickableWidget::mouseDoubleClickEvent(QMouseEvent* event) { - if (event->button() == Qt::MouseButton::LeftButton) { - emit this->doubleClicked(); + QWidget::mouseDoubleClickEvent(event); } - - QWidget::mouseDoubleClickEvent(event); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/ErrorDialogue.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/ErrorDialogue.cpp index d5517f56..e8724c4d 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/ErrorDialogue.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/ErrorDialogue.cpp @@ -7,58 +7,64 @@ #include "src/Helpers/Paths.hpp" #include "src/Exceptions/Exception.hpp" -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + using Bloom::Exceptions::Exception; -using Bloom::Exceptions::Exception; + ErrorDialogue::ErrorDialogue( + const QString& windowTitle, + const QString& errorMessage, + QWidget* parent + ): QDialog(parent) { + this->setObjectName("error-dialogue"); + this->setAttribute(Qt::WA_DeleteOnClose, true); + this->setWindowTitle(windowTitle); -ErrorDialogue::ErrorDialogue( - const QString& windowTitle, - const QString& errorMessage, - QWidget* parent -): QDialog(parent) { - this->setObjectName("error-dialogue"); - this->setAttribute(Qt::WA_DeleteOnClose, true); - this->setWindowTitle(windowTitle); + auto dialogueUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/UiFiles/ErrorDialogue.ui" + ) + ); - auto dialogueUiFile = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/UiFiles/ErrorDialogue.ui" - ) - ); + auto dialogueStylesheet = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/Stylesheets/ErrorDialogue.qss" + ) + ); - auto dialogueStylesheet = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/Stylesheets/ErrorDialogue.qss" - ) - ); + if (!dialogueUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open ErrorDialogue UI file"); + } - if (!dialogueUiFile.open(QFile::ReadOnly)) { - throw Exception("Failed to open ErrorDialogue UI file"); + if (!dialogueStylesheet.open(QFile::ReadOnly)) { + throw Exception("Failed to open ErrorDialogue stylesheet file"); + } + + this->setStyleSheet(dialogueStylesheet.readAll()); + + auto uiLoader = UiLoader(this); + this->container = uiLoader.load(&dialogueUiFile, this); + + this->errorMessageDescriptionLabel = this->container->findChild( + "error-message-description-label" + ); + this->okButton = this->container->findChild("ok-btn"); + + this->container->setContentsMargins(15, 10, 15, 15); + + this->errorMessageDescriptionLabel->setText(errorMessage); + + QObject::connect(this->okButton, &QPushButton::clicked, this, &QDialog::close); } - if (!dialogueStylesheet.open(QFile::ReadOnly)) { - throw Exception("Failed to open ErrorDialogue stylesheet file"); + void ErrorDialogue::showEvent(QShowEvent* event) { + const auto containerSize = this->container->sizeHint(); + const auto windowSize = QSize( + std::max(containerSize.width(), 500), + std::max(containerSize.height(), 100) + ); + + this->setFixedSize(windowSize); + this->container->setFixedSize(windowSize); } - - this->setStyleSheet(dialogueStylesheet.readAll()); - - auto uiLoader = UiLoader(this); - this->container = uiLoader.load(&dialogueUiFile, this); - - this->errorMessageDescriptionLabel = this->container->findChild("error-message-description-label"); - this->okButton = this->container->findChild("ok-btn"); - - this->container->setContentsMargins(15, 10, 15, 15); - - this->errorMessageDescriptionLabel->setText(errorMessage); - - QObject::connect(this->okButton, &QPushButton::clicked, this, &QDialog::close); -} - -void ErrorDialogue::showEvent(QShowEvent* event) { - const auto containerSize = this->container->sizeHint(); - const auto windowSize = QSize(std::max(containerSize.width(), 500), std::max(containerSize.height(), 100)); - - this->setFixedSize(windowSize); - this->container->setFixedSize(windowSize); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/LabeledSeparator.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/LabeledSeparator.cpp index e296ef29..9d77539d 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/LabeledSeparator.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/LabeledSeparator.cpp @@ -1,35 +1,36 @@ #include "LabeledSeparator.hpp" -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + LabeledSeparator::LabeledSeparator(QString title, QWidget* parent): title(std::move(title)), QWidget(parent) { + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + this->setFixedHeight(LabeledSeparator::DEFAULT_HEIGHT); + } -LabeledSeparator::LabeledSeparator(QString title, QWidget* parent): title(std::move(title)), QWidget(parent) { - this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - this->setFixedHeight(LabeledSeparator::DEFAULT_HEIGHT); -} - -void LabeledSeparator::paintEvent(QPaintEvent* event) { - auto painter = QPainter(this); - this->drawWidget(painter); -} - -void LabeledSeparator::drawWidget(QPainter& painter) { - const auto fontMetrics = painter.fontMetrics(); - const auto titleSize = fontMetrics.size(Qt::TextFlag::TextSingleLine, this->title); - const auto titleRect = QRect( - QPoint(this->marginLeft, (this->height() - titleSize.height()) / 2), - titleSize - ); - - const auto lineYPosition = titleRect.y() + (titleRect.height() / 2); - const auto line = QLine( - titleRect.right() + 8, - lineYPosition, - this->width() - this->marginRight, - lineYPosition - ); - - painter.drawText(titleRect, Qt::AlignCenter, this->title); - - painter.setPen(this->lineColor); - painter.drawLine(line); + void LabeledSeparator::paintEvent(QPaintEvent* event) { + auto painter = QPainter(this); + this->drawWidget(painter); + } + + void LabeledSeparator::drawWidget(QPainter& painter) { + const auto fontMetrics = painter.fontMetrics(); + const auto titleSize = fontMetrics.size(Qt::TextFlag::TextSingleLine, this->title); + const auto titleRect = QRect( + QPoint(this->marginLeft, (this->height() - titleSize.height()) / 2), + titleSize + ); + + const auto lineYPosition = titleRect.y() + (titleRect.height() / 2); + const auto line = QLine( + titleRect.right() + 8, + lineYPosition, + this->width() - this->marginRight, + lineYPosition + ); + + painter.drawText(titleRect, Qt::AlignCenter, this->title); + + painter.setPen(this->lineColor); + painter.drawLine(line); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/PanelWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/PanelWidget.cpp index c895a202..ff2dfe5e 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/PanelWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/PanelWidget.cpp @@ -2,138 +2,139 @@ #include -using namespace Bloom::Widgets; - -PanelWidget::PanelWidget(QWidget* parent): QFrame(parent) { - this->setMouseTracking(false); - this->setAttribute(Qt::WA_Hover, true); -} - -void PanelWidget::setMinimumResize(int minimumResize) { - this->minimumResize = minimumResize; - - const auto currentSize = this->size(); - - if (this->panelType == PanelWidgetType::LEFT && currentSize.width() < this->minimumResize) { - this->setFixedWidth(this->minimumResize); - - } else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() < this->minimumResize) { - this->setFixedHeight(this->minimumResize); +namespace Bloom::Widgets +{ + PanelWidget::PanelWidget(QWidget* parent): QFrame(parent) { + this->setMouseTracking(false); + this->setAttribute(Qt::WA_Hover, true); } -} -void PanelWidget::setMaximumResize(int maximumResize) { - this->maximumResize = maximumResize; + void PanelWidget::setMinimumResize(int minimumResize) { + this->minimumResize = minimumResize; - const auto currentSize = this->size(); + const auto currentSize = this->size(); - if (this->panelType == PanelWidgetType::LEFT && currentSize.width() > this->maximumResize) { - this->setFixedWidth(this->maximumResize); + if (this->panelType == PanelWidgetType::LEFT && currentSize.width() < this->minimumResize) { + this->setFixedWidth(this->minimumResize); - } else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() > this->maximumResize) { - this->setFixedHeight(this->maximumResize); + } else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() < this->minimumResize) { + this->setFixedHeight(this->minimumResize); + } } -} -void PanelWidget::setPanelType(PanelWidgetType panelType) { - this->panelType = panelType; + void PanelWidget::setMaximumResize(int maximumResize) { + this->maximumResize = maximumResize; - if (this->panelType == PanelWidgetType::LEFT) { - this->resizeCursor = Qt::SplitHCursor; - this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); + const auto currentSize = this->size(); - } else { - this->resizeCursor = Qt::SplitVCursor; - this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + if (this->panelType == PanelWidgetType::LEFT && currentSize.width() > this->maximumResize) { + this->setFixedWidth(this->maximumResize); + + } else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() > this->maximumResize) { + this->setFixedHeight(this->maximumResize); + } } -} -bool PanelWidget::event(QEvent* event) { - if (event->type() == QEvent::Type::HoverMove) { - auto hoverEvent = static_cast(event); - if (this->resizingActive || this->isPositionWithinHandleArea(hoverEvent->position().toPoint())) { - this->setCursor(this->resizeCursor); + void PanelWidget::setPanelType(PanelWidgetType panelType) { + this->panelType = panelType; + + if (this->panelType == PanelWidgetType::LEFT) { + this->resizeCursor = Qt::SplitHCursor; + this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); } else { + this->resizeCursor = Qt::SplitVCursor; + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + } + } + + bool PanelWidget::event(QEvent* event) { + if (event->type() == QEvent::Type::HoverMove) { + auto hoverEvent = static_cast(event); + if (this->resizingActive || this->isPositionWithinHandleArea(hoverEvent->position().toPoint())) { + this->setCursor(this->resizeCursor); + + } else { + this->setCursor(Qt::ArrowCursor); + } + } + + return QFrame::event(event); + } + + void PanelWidget::mousePressEvent(QMouseEvent* event) { + const auto position = event->pos(); + + if (event->buttons() & Qt::LeftButton && this->isPositionWithinHandleArea(position)) { + this->resizingActive = true; + + switch (this->panelType) { + case PanelWidgetType::LEFT: { + this->resizingOffset = this->width() - position.x(); + break; + } + case PanelWidgetType::BOTTOM: { + this->resizingOffset = position.y(); + break; + } + } + } + } + + void PanelWidget::mouseReleaseEvent(QMouseEvent* event) { + if (this->resizingActive) { + this->resizingActive = false; + this->resizingOffset = 0; this->setCursor(Qt::ArrowCursor); } } - return QFrame::event(event); -} + void PanelWidget::mouseMoveEvent(QMouseEvent* event) { + if (this->resizingActive) { + const auto position = event->pos(); -void PanelWidget::mousePressEvent(QMouseEvent* event) { - const auto position = event->pos(); + if (this->panelType == PanelWidgetType::LEFT) { + this->setFixedWidth( + std::max( + this->minimumResize, + std::min(this->maximumResize, position.x() + this->resizingOffset) + ) + ); - if (event->buttons() & Qt::LeftButton && this->isPositionWithinHandleArea(position)) { - this->resizingActive = true; - - switch (this->panelType) { - case PanelWidgetType::LEFT: { - this->resizingOffset = this->width() - position.x(); - break; - } - case PanelWidgetType::BOTTOM: { - this->resizingOffset = position.y(); - break; + } else if (this->panelType == PanelWidgetType::BOTTOM) { + this->setFixedHeight( + std::max( + this->minimumResize, + std::min(this->maximumResize, this->height() + (-position.y()) + this->resizingOffset) + ) + ); } } } -} -void PanelWidget::mouseReleaseEvent(QMouseEvent* event) { - if (this->resizingActive) { - this->resizingActive = false; - this->resizingOffset = 0; - this->setCursor(Qt::ArrowCursor); - } -} - -void PanelWidget::mouseMoveEvent(QMouseEvent* event) { - if (this->resizingActive) { - const auto position = event->pos(); + std::pair PanelWidget::getHandleArea() const { + const auto currentSize = this->size(); if (this->panelType == PanelWidgetType::LEFT) { - this->setFixedWidth( - std::max( - this->minimumResize, - std::min(this->maximumResize, position.x() + this->resizingOffset) - ) + return std::pair( + QPoint(currentSize.width() - this->handleSize, 0), + QPoint(currentSize.width(), currentSize.height()) ); - } else if (this->panelType == PanelWidgetType::BOTTOM) { - this->setFixedHeight( - std::max( - this->minimumResize, - std::min(this->maximumResize, this->height() + (-position.y()) + this->resizingOffset) - ) + } else { + return std::pair( + QPoint(0, 0), + QPoint(currentSize.width(), this->handleSize) ); } } -} -std::pair PanelWidget::getHandleArea() const { - const auto currentSize = this->size(); + bool PanelWidget::isPositionWithinHandleArea(const QPoint& position) const { + const auto handleArea = this->getHandleArea(); - if (this->panelType == PanelWidgetType::LEFT) { - return std::pair( - QPoint(currentSize.width() - this->handleSize, 0), - QPoint(currentSize.width(), currentSize.height()) - ); - - } else { - return std::pair( - QPoint(0, 0), - QPoint(currentSize.width(), this->handleSize) + return ( + position.x() >= handleArea.first.x() && position.x() <= handleArea.second.x() + && position.y() >= handleArea.first.y() && position.y() <= handleArea.second.y() ); } } - -bool PanelWidget::isPositionWithinHandleArea(const QPoint& position) const { - const auto handleArea = this->getHandleArea(); - - return ( - position.x() >= handleArea.first.x() && position.x() <= handleArea.second.x() - && position.y() >= handleArea.first.y() && position.y() <= handleArea.second.y() - ); -} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/RotatableLabel.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/RotatableLabel.cpp index 66cb43fd..2fd81492 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/RotatableLabel.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/RotatableLabel.cpp @@ -2,60 +2,61 @@ #include -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + void RotatableLabel::paintEvent(QPaintEvent* event) { + auto painter = QPainter(this); + static auto containerSize = this->getContainerSize(); + static auto textSize = QLabel::minimumSizeHint(); + static auto margins = this->contentsMargins(); + painter.setClipRect(0, 0, containerSize.width(), containerSize.height()); + painter.save(); + painter.setPen(Qt::PenStyle::SolidLine); + painter.setPen(QColor(this->isEnabled() ? "#afb1b3" : "#808484")); + painter.translate(std::ceil(containerSize.width() / 2), std::ceil(containerSize.height() / 2)); + painter.rotate(this->angle); + painter.drawText( + -(textSize.width() / 2) + margins.left(), + (textSize.height() / 2) + margins.top(), + this->text() + ); -void RotatableLabel::paintEvent(QPaintEvent* event) { - auto painter = QPainter(this); - static auto containerSize = this->getContainerSize(); - static auto textSize = QLabel::minimumSizeHint(); - static auto margins = this->contentsMargins(); - painter.setClipRect(0, 0, containerSize.width(), containerSize.height()); - painter.save(); - painter.setPen(Qt::PenStyle::SolidLine); - painter.setPen(QColor(this->isEnabled() ? "#afb1b3" : "#808484")); - painter.translate(std::ceil(containerSize.width() / 2), std::ceil(containerSize.height() / 2)); - painter.rotate(this->angle); - painter.drawText( - -(textSize.width() / 2) + margins.left(), - (textSize.height() / 2) + margins.top(), - this->text() - ); - - painter.restore(); -} - -QSize RotatableLabel::getContainerSize() const { - auto size = QSize(); - auto textSize = QLabel::sizeHint(); - - if (this->angle % 360 == 0 || this->angle % 180 == 0) { - size = textSize; - - } else if (this->angle % 90 == 0) { - size.setHeight(textSize.width()); - size.setWidth(textSize.height()); - - } else { - auto angle = this->angle; - - if (angle > 90) { - float angleMultiplier = static_cast(angle) / 90; - angleMultiplier = static_cast(angleMultiplier) - std::floor(angleMultiplier); - angle = static_cast(90 * angleMultiplier); - } - - auto angleRadians = angle * (M_PI / 180); - - size.setWidth(static_cast( - std::cos(angleRadians) * textSize.width() - + std::ceil(std::sin(angleRadians) * textSize.height()) - )) - ; - size.setHeight(static_cast( - std::sin(angleRadians) * textSize.width() - + std::ceil(std::cos(angleRadians) * textSize.height()) - )); + painter.restore(); } - return size; + QSize RotatableLabel::getContainerSize() const { + auto size = QSize(); + auto textSize = QLabel::sizeHint(); + + if (this->angle % 360 == 0 || this->angle % 180 == 0) { + size = textSize; + + } else if (this->angle % 90 == 0) { + size.setHeight(textSize.width()); + size.setWidth(textSize.height()); + + } else { + auto angle = this->angle; + + if (angle > 90) { + float angleMultiplier = static_cast(angle) / 90; + angleMultiplier = static_cast(angleMultiplier) - std::floor(angleMultiplier); + angle = static_cast(90 * angleMultiplier); + } + + auto angleRadians = angle * (M_PI / 180); + + size.setWidth(static_cast( + std::cos(angleRadians) * textSize.width() + + std::ceil(std::sin(angleRadians) * textSize.height()) + )) + ; + size.setHeight(static_cast( + std::sin(angleRadians) * textSize.width() + + std::ceil(std::cos(angleRadians) * textSize.height()) + )); + } + + return size; + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.cpp index 14ccca29..6a856027 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.cpp @@ -2,22 +2,23 @@ #include -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + SvgToolButton::SvgToolButton(QWidget* parent): QToolButton(parent) { + this->setButtonWidth(10); + this->setButtonHeight(10); + } -SvgToolButton::SvgToolButton(QWidget* parent): QToolButton(parent) { - this->setButtonWidth(10); - this->setButtonHeight(10); -} - -void SvgToolButton::childEvent(QChildEvent* childEvent) { - if ((childEvent->added() || childEvent->polished()) && childEvent->child()->isWidgetType()) { - /* - * If a menu widget has been added as a child to this SvgToolButton, associate the menu with the button - * via QToolButton::setMenu(). - */ - auto* menuWidget = qobject_cast(childEvent->child()); - if (menuWidget != nullptr && menuWidget != this->menu()) { - this->setMenu(menuWidget); + void SvgToolButton::childEvent(QChildEvent* childEvent) { + if ((childEvent->added() || childEvent->polished()) && childEvent->child()->isWidgetType()) { + /* + * If a menu widget has been added as a child to this SvgToolButton, associate the menu with the button + * via QToolButton::setMenu(). + */ + auto* menuWidget = qobject_cast(childEvent->child()); + if (menuWidget != nullptr && menuWidget != this->menu()) { + this->setMenu(menuWidget); + } } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/SvgWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/SvgWidget.cpp index 60e74744..2a0806e2 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/SvgWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/SvgWidget.cpp @@ -3,71 +3,76 @@ #include #include -using namespace Bloom::Widgets; - -SvgWidget::SvgWidget(QWidget* parent): QFrame(parent) { - this->renderer.setAspectRatioMode(Qt::AspectRatioMode::KeepAspectRatioByExpanding); -} - -void SvgWidget::startSpin() { - if (this->spinningAnimation == nullptr) { - this->spinningAnimation = new QPropertyAnimation(this, "angle", this); - this->spinningAnimation->setDuration(2000); - this->spinningAnimation->setStartValue(0); - this->spinningAnimation->setEndValue(360); - - QObject::connect(this->spinningAnimation, &QPropertyAnimation::finished, this, [this] { - this->spinningAnimation->start(); - }); +namespace Bloom::Widgets +{ + SvgWidget::SvgWidget(QWidget* parent): QFrame(parent) { + this->renderer.setAspectRatioMode(Qt::AspectRatioMode::KeepAspectRatioByExpanding); } - this->spinningAnimation->start(); -} + void SvgWidget::startSpin() { + if (this->spinningAnimation == nullptr) { + this->spinningAnimation = new QPropertyAnimation(this, "angle", this); + this->spinningAnimation->setDuration(2000); + this->spinningAnimation->setStartValue(0); + this->spinningAnimation->setEndValue(360); -void SvgWidget::stopSpin() { - if (this->spinningAnimation != nullptr) { - this->spinningAnimation->stop(); - this->setAngle(0); - } -} - -void SvgWidget::paintEvent(QPaintEvent* paintEvent) { - auto painter = QPainter(this); - auto svgSize = this->renderer.defaultSize(); - auto margins = this->contentsMargins(); - const auto containerSize = this->frameSize(); - - painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true); - - if (this->angle % 360 != 0) { - painter.translate( - std::ceil(static_cast(containerSize.width() / 2)), - std::ceil(static_cast(containerSize.height() / 2)) - ); - painter.rotate(this->angle); - painter.translate( - -std::ceil(static_cast(containerSize.width() / 2)), - -std::ceil(static_cast(containerSize.height() / 2)) - ); - } - - this->renderer.render(&painter, QRectF( - std::ceil(static_cast(containerSize.width() - svgSize.width()) / 2 + static_cast(margins.left())), - std::ceil(static_cast(containerSize.height() - svgSize.height()) / 2 + static_cast(margins.top())), - svgSize.width(), - svgSize.height() - )); -} - -void SvgWidget::changeEvent(QEvent* event) { - if (event->type() == QEvent::EnabledChange && !this->disabledSvgFilePath.isEmpty()) { - if (!this->isEnabled()) { - this->renderer.load(this->disabledSvgFilePath); - - } else { - this->renderer.load(this->svgFilePath); + QObject::connect(this->spinningAnimation, &QPropertyAnimation::finished, this, [this] { + this->spinningAnimation->start(); + }); } - this->repaint(); + this->spinningAnimation->start(); + } + + void SvgWidget::stopSpin() { + if (this->spinningAnimation != nullptr) { + this->spinningAnimation->stop(); + this->setAngle(0); + } + } + + void SvgWidget::paintEvent(QPaintEvent* paintEvent) { + auto painter = QPainter(this); + auto svgSize = this->renderer.defaultSize(); + auto margins = this->contentsMargins(); + const auto containerSize = this->frameSize(); + + painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true); + + if (this->angle % 360 != 0) { + painter.translate( + std::ceil(static_cast(containerSize.width() / 2)), + std::ceil(static_cast(containerSize.height() / 2)) + ); + painter.rotate(this->angle); + painter.translate( + -std::ceil(static_cast(containerSize.width() / 2)), + -std::ceil(static_cast(containerSize.height() / 2)) + ); + } + + this->renderer.render(&painter, QRectF( + std::ceil( + static_cast(containerSize.width() - svgSize.width()) / 2 + static_cast(margins.left()) + ), + std::ceil( + static_cast(containerSize.height() - svgSize.height()) / 2 + static_cast(margins.top()) + ), + svgSize.width(), + svgSize.height() + )); + } + + void SvgWidget::changeEvent(QEvent* event) { + if (event->type() == QEvent::EnabledChange && !this->disabledSvgFilePath.isEmpty()) { + if (!this->isEnabled()) { + this->renderer.load(this->disabledSvgFilePath); + + } else { + this->renderer.load(this->svgFilePath); + } + + this->repaint(); + } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp index 1fc2c3d2..150ae3cc 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp @@ -4,109 +4,115 @@ #include "ByteItem.hpp" -using namespace Bloom::Widgets; - -AnnotationItem::AnnotationItem( - std::uint32_t startAddress, - std::size_t size, - QString labelText, - AnnotationItemPosition position -): - QGraphicsItem(nullptr), - startAddress(startAddress), - size(size), - endAddress(static_cast(startAddress + size - 1)), - labelText(std::move(labelText)), - position(position), - width(static_cast((ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) * size - ByteItem::RIGHT_MARGIN)), - height(position == AnnotationItemPosition::TOP ? AnnotationItem::TOP_HEIGHT : AnnotationItem::BOTTOM_HEIGHT -) { - this->setAcceptHoverEvents(true); - this->setToolTip(this->labelText); -} - -AnnotationItem::AnnotationItem( - const Targets::TargetMemoryAddressRange& addressRange, - const QString& labelText, - AnnotationItemPosition position -): AnnotationItem( - addressRange.startAddress, - addressRange.endAddress - addressRange.startAddress + 1, - labelText, - position -) {} - -AnnotationItem::AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position) -: AnnotationItem( - focusedMemoryRegion.addressRange, - focusedMemoryRegion.name, - position -) {} - -void AnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { - auto lineColor = this->getLineColor(); - auto labelFontColor = this->getLabelFontColor(); - - auto font = painter->font(); - font.setPixelSize(this->getLabelFontSize()); - painter->setFont(font); - - const auto isEnabled = this->isEnabled(); - - lineColor.setAlpha(isEnabled ? 255 : 100); - labelFontColor.setAlpha(isEnabled ? 255 : 100); - - const auto fontMetrics = painter->fontMetrics(); - auto labelSize = fontMetrics.size(Qt::TextSingleLine, this->labelText); - if (labelSize.width() > this->width) { - labelSize.setWidth(this->width); - this->labelText = fontMetrics.elidedText(this->labelText, Qt::TextElideMode::ElideRight, this->width); +namespace Bloom::Widgets +{ + AnnotationItem::AnnotationItem( + std::uint32_t startAddress, + std::size_t size, + QString labelText, + AnnotationItemPosition position + ): + QGraphicsItem(nullptr), + startAddress(startAddress), + size(size), + endAddress(static_cast(startAddress + size - 1)), + labelText(std::move(labelText)), + position(position), + width(static_cast((ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) * size - ByteItem::RIGHT_MARGIN)), + height(position == AnnotationItemPosition::TOP ? AnnotationItem::TOP_HEIGHT : AnnotationItem::BOTTOM_HEIGHT + ) { + this->setAcceptHoverEvents(true); + this->setToolTip(this->labelText); } - constexpr auto verticalLineLength = 5; - const auto verticalLineYStart = this->position == AnnotationItemPosition::BOTTOM ? 0 : AnnotationItem::TOP_HEIGHT; - const auto verticalLineYEnd = this->position == AnnotationItemPosition::BOTTOM ? - verticalLineLength : AnnotationItem::TOP_HEIGHT - verticalLineLength; + AnnotationItem::AnnotationItem( + const Targets::TargetMemoryAddressRange& addressRange, + const QString& labelText, + AnnotationItemPosition position + ): AnnotationItem( + addressRange.startAddress, + addressRange.endAddress - addressRange.startAddress + 1, + labelText, + position + ) {} - const auto labelRect = QRect( - (this->width - labelSize.width()) / 2, - verticalLineYEnd - (this->position == AnnotationItemPosition::BOTTOM ? -6: labelSize.height() + 6), - labelSize.width(), - labelSize.height() - ); + AnnotationItem::AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position) + : AnnotationItem( + focusedMemoryRegion.addressRange, + focusedMemoryRegion.name, + position + ) {} - painter->setPen(lineColor); + void AnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { + auto lineColor = this->getLineColor(); + auto labelFontColor = this->getLabelFontColor(); - if (this->size > 1) { - painter->drawLine(QLine( - ByteItem::WIDTH / 2, - verticalLineYStart, - ByteItem::WIDTH / 2, - verticalLineYEnd - )); + auto font = painter->font(); + font.setPixelSize(this->getLabelFontSize()); + painter->setFont(font); + + const auto isEnabled = this->isEnabled(); + + lineColor.setAlpha(isEnabled ? 255 : 100); + labelFontColor.setAlpha(isEnabled ? 255 : 100); + + const auto fontMetrics = painter->fontMetrics(); + auto labelSize = fontMetrics.size(Qt::TextSingleLine, this->labelText); + if (labelSize.width() > this->width) { + labelSize.setWidth(this->width); + this->labelText = fontMetrics.elidedText( + this->labelText, + Qt::TextElideMode::ElideRight, + this->width + ); + } + + constexpr auto verticalLineLength = 5; + const auto verticalLineYStart = this->position == AnnotationItemPosition::BOTTOM ? 0 + : AnnotationItem::TOP_HEIGHT; + const auto verticalLineYEnd = this->position == AnnotationItemPosition::BOTTOM ? + verticalLineLength : AnnotationItem::TOP_HEIGHT - verticalLineLength; + + const auto labelRect = QRect( + (this->width - labelSize.width()) / 2, + verticalLineYEnd - (this->position == AnnotationItemPosition::BOTTOM ? -6: labelSize.height() + 6), + labelSize.width(), + labelSize.height() + ); + + painter->setPen(lineColor); + + if (this->size > 1) { + painter->drawLine(QLine( + ByteItem::WIDTH / 2, + verticalLineYStart, + ByteItem::WIDTH / 2, + verticalLineYEnd + )); + + painter->drawLine(QLine( + this->width - (ByteItem::WIDTH / 2), + verticalLineYStart, + this->width - (ByteItem::WIDTH / 2), + verticalLineYEnd + )); + + painter->drawLine(QLine( + ByteItem::WIDTH / 2, + verticalLineYEnd, + (ByteItem::WIDTH / 2) + (this->width - ByteItem::WIDTH), + verticalLineYEnd + )); + } painter->drawLine(QLine( - this->width - (ByteItem::WIDTH / 2), - verticalLineYStart, - this->width - (ByteItem::WIDTH / 2), - verticalLineYEnd - )); - - painter->drawLine(QLine( - ByteItem::WIDTH / 2, + this->width / 2, verticalLineYEnd, - (ByteItem::WIDTH / 2) + (this->width - ByteItem::WIDTH), - verticalLineYEnd + this->width / 2, + (this->position == AnnotationItemPosition::BOTTOM ? verticalLineYEnd + 4 : verticalLineYEnd - 4) )); + + painter->setPen(labelFontColor); + painter->drawText(labelRect, Qt::AlignCenter, this->labelText); } - - painter->drawLine(QLine( - this->width / 2, - verticalLineYEnd, - this->width / 2, - (this->position == AnnotationItemPosition::BOTTOM ? verticalLineYEnd + 4 : verticalLineYEnd - 4) - )); - - painter->setPen(labelFontColor); - painter->drawText(labelRect, Qt::AlignCenter, this->labelText); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.cpp index 8d9af344..1a39b548 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.cpp @@ -1,71 +1,72 @@ #include "ByteAddressContainer.hpp" -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + void ByteAddressContainer::adjustAddressLabels( + const std::map>& byteItemsByRowIndex + ) { + static const auto margins = QMargins(0, 10, 0, 0); -void ByteAddressContainer::adjustAddressLabels( - const std::map>& byteItemsByRowIndex -) { - static const auto margins = QMargins(0, 10, 0, 0); + const auto newRowCount = byteItemsByRowIndex.size(); + const auto layoutItemMaxIndex = static_cast(this->addressItemsByRowIndex.size() - 1); - const auto newRowCount = byteItemsByRowIndex.size(); - const auto layoutItemMaxIndex = static_cast(this->addressItemsByRowIndex.size() - 1); + for (const auto& mappingPair : byteItemsByRowIndex) { + const auto rowIndex = static_cast(mappingPair.first); + const auto& byteItems = mappingPair.second; - for (const auto& mappingPair : byteItemsByRowIndex) { - const auto rowIndex = static_cast(mappingPair.first); - const auto& byteItems = mappingPair.second; + if (byteItems.empty()) { + continue; + } - if (byteItems.empty()) { - continue; + ByteAddressItem* addressLabel = nullptr; + if (static_cast(rowIndex) > layoutItemMaxIndex) { + addressLabel = new ByteAddressItem(this); + this->addressItemsByRowIndex.insert(std::pair(rowIndex, addressLabel)); + + } else { + addressLabel = this->addressItemsByRowIndex.at(rowIndex); + } + + const auto& firstByteItem = byteItems.front(); + addressLabel->setAddressHex(firstByteItem->relativeAddressHex); + addressLabel->setPos( + 0, + firstByteItem->pos().y() + ); } - ByteAddressItem* addressLabel = nullptr; - if (static_cast(rowIndex) > layoutItemMaxIndex) { - addressLabel = new ByteAddressItem(this); - this->addressItemsByRowIndex.insert(std::pair(rowIndex, addressLabel)); + // Delete any address items we no longer need + const auto addressItemCount = this->addressItemsByRowIndex.size(); - } else { - addressLabel = this->addressItemsByRowIndex.at(rowIndex); + if (newRowCount > 0 && newRowCount < addressItemCount) { + for (auto i = (addressItemCount - 1); i >= newRowCount; i--) { + delete this->addressItemsByRowIndex.at(i); + this->addressItemsByRowIndex.erase(i); + } } - const auto& firstByteItem = byteItems.front(); - addressLabel->setAddressHex(firstByteItem->relativeAddressHex); - addressLabel->setPos( + this->update(); + } + + void ByteAddressContainer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { + static const auto backgroundColor = QColor(0x35, 0x36, 0x33); + static const auto borderColor = QColor(0x41, 0x42, 0x3F); + + painter->setPen(Qt::PenStyle::NoPen); + painter->setBrush(backgroundColor); + painter->drawRect( 0, - firstByteItem->pos().y() + 0, + ByteAddressContainer::WIDTH, + static_cast(this->boundingRect().height()) + ); + + painter->setPen(borderColor); + painter->drawLine( + ByteAddressContainer::WIDTH - 1, + 0, + ByteAddressContainer::WIDTH - 1, + static_cast(this->boundingRect().height()) ); } - - // Delete any address items we no longer need - const auto addressItemCount = this->addressItemsByRowIndex.size(); - - if (newRowCount > 0 && newRowCount < addressItemCount) { - for (auto i = (addressItemCount - 1); i >= newRowCount; i--) { - delete this->addressItemsByRowIndex.at(i); - this->addressItemsByRowIndex.erase(i); - } - } - - this->update(); -} - -void ByteAddressContainer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { - static const auto backgroundColor = QColor(0x35, 0x36, 0x33); - static const auto borderColor = QColor(0x41, 0x42, 0x3F); - - painter->setPen(Qt::PenStyle::NoPen); - painter->setBrush(backgroundColor); - painter->drawRect( - 0, - 0, - ByteAddressContainer::WIDTH, - static_cast(this->boundingRect().height()) - ); - - painter->setPen(borderColor); - painter->drawLine( - ByteAddressContainer::WIDTH - 1, - 0, - ByteAddressContainer::WIDTH - 1, - static_cast(this->boundingRect().height()) - ); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.cpp index dc17d545..eca2bbe3 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.cpp @@ -1,28 +1,31 @@ #include "ByteAddressItem.hpp" #include -#include -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + void ByteAddressItem::setAddressHex(const QString& addressHex) { + this->setCacheMode( + QGraphicsItem::CacheMode::ItemCoordinateCache, + QSize(ByteAddressItem::WIDTH, ByteAddressItem::HEIGHT) + ); + this->addressHex = addressHex; + } -void ByteAddressItem::setAddressHex(const QString& addressHex) { - this->setCacheMode( - QGraphicsItem::CacheMode::ItemCoordinateCache, - QSize(ByteAddressItem::WIDTH, ByteAddressItem::HEIGHT) - ); - this->addressHex = addressHex; -} - -void ByteAddressItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { - painter->setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true); - - static const auto widgetRect = this->boundingRect(); - static auto fontColor = QColor(0x8F, 0x91, 0x92); - static auto font = QFont("'Ubuntu', sans-serif"); - font.setPixelSize(12); - fontColor.setAlpha(!this->isEnabled() ? 100 : 255); - - painter->setFont(font); - painter->setPen(fontColor); - painter->drawText(widgetRect, Qt::AlignCenter, this->addressHex); + void ByteAddressItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { + painter->setRenderHints( + QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, + true + ); + + static const auto widgetRect = this->boundingRect(); + static auto fontColor = QColor(0x8F, 0x91, 0x92); + static auto font = QFont("'Ubuntu', sans-serif"); + font.setPixelSize(12); + fontColor.setAlpha(!this->isEnabled() ? 100 : 255); + + painter->setFont(font); + painter->setPen(fontColor); + painter->drawText(widgetRect, Qt::AlignCenter, this->addressHex); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp index 5a3661b7..21e50bd3 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp @@ -1,166 +1,175 @@ #include "ByteItem.hpp" #include -#include -using namespace Bloom::Widgets; - -ByteItem::ByteItem( - std::size_t byteIndex, - std::uint32_t address, - std::optional& currentStackPointer, - std::optional& hoveredByteItem, - std::optional& hoveredAnnotationItem, - std::set& highlightedAddresses, - const HexViewerWidgetSettings& settings -): -QGraphicsItem(nullptr), -byteIndex(byteIndex), -address(address), -currentStackPointer(currentStackPointer), -hoveredByteItem(hoveredByteItem), -hoveredAnnotationItem(hoveredAnnotationItem), -highlightedAddresses(highlightedAddresses), -settings(settings) +namespace Bloom::Widgets { - this->setCacheMode( - QGraphicsItem::CacheMode::ItemCoordinateCache, - QSize(ByteItem::WIDTH, ByteItem::HEIGHT) - ); - this->setAcceptHoverEvents(true); + ByteItem::ByteItem( + std::size_t byteIndex, + std::uint32_t address, + std::optional& currentStackPointer, + std::optional& hoveredByteItem, + std::optional& hoveredAnnotationItem, + std::set& highlightedAddresses, + const HexViewerWidgetSettings& settings + ) + : QGraphicsItem(nullptr) + , byteIndex(byteIndex) + , address(address) + , currentStackPointer(currentStackPointer) + , hoveredByteItem(hoveredByteItem) + , hoveredAnnotationItem(hoveredAnnotationItem) + , highlightedAddresses(highlightedAddresses) + , settings(settings) + { + this->setCacheMode( + QGraphicsItem::CacheMode::ItemCoordinateCache, + QSize(ByteItem::WIDTH, ByteItem::HEIGHT) + ); + this->setAcceptHoverEvents(true); - this->addressHex = "0x" + QString::number(this->address, 16).rightJustified(8, '0').toUpper(); - this->relativeAddressHex = "0x" + QString::number(this->byteIndex, 16).rightJustified(8, '0').toUpper(); + this->addressHex = "0x" + QString::number(this->address, 16).rightJustified( + 8, + '0' + ).toUpper(); + this->relativeAddressHex = "0x" + QString::number(this->byteIndex, 16).rightJustified( + 8, + '0' + ).toUpper(); - this->setSelected(false); -} - -void ByteItem::setValue(unsigned char value) { - this->valueChanged = this->valueInitialised && this->value != value; - - this->value = value; - this->hexValue = QString::number(this->value, 16).rightJustified(2, '0').toUpper(); - this->asciiValue = (this->value >= 32 && this->value <= 126) - ? std::optional("'" + QString(QChar(this->value)) + "'") : std::nullopt; - - this->valueInitialised = this->excludedMemoryRegion == nullptr; - this->update(); -} - -void ByteItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { - painter->setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true); - painter->setPen(Qt::PenStyle::NoPen); - - // TODO: This code could do with some tidying. It's getting quite messy. - - static const auto widgetRect = this->boundingRect(); - static const auto standardTextColor = QColor(0xAF, 0xB1, 0xB3); - static const auto valueChangedTextColor = QColor(0x54, 0x7F, 0xBA); - static auto font = QFont("'Ubuntu', sans-serif"); - - static const auto highlightedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255); - static const auto focusedRegionBackgroundColor = QColor(0x44, 0x44, 0x41, 255); - static const auto stackMemoryBackgroundColor = QColor(0x67, 0x57, 0x20, 210); - static const auto hoveredBackgroundColor = QColor(0x8E, 0x8B, 0x83, 70); - static const auto hoveredNeighbourBackgroundColor = QColor(0x8E, 0x8B, 0x83, 30); - static const auto hoveredAnnotationBackgroundColor = QColor(0x8E, 0x8B, 0x83, 50); - - const auto isEnabled = this->isEnabled(); - - const auto highlightingEnabled = !this->highlightedAddresses.empty(); - const auto highlightedByte = highlightingEnabled && this->highlightedAddresses.contains(this->address); - - auto textColor = standardTextColor; - auto asciiTextColor = QColor(0xA7, 0x77, 0x26); - - auto backgroundColor = std::optional(); - - font.setPixelSize(11); - painter->setFont(font); - - if (highlightedByte) { - backgroundColor = highlightedBackgroundColor; - asciiTextColor = standardTextColor; - - } else if (this->settings.highlightStackMemory && this->currentStackPointer.has_value() - && this->address > this->currentStackPointer - ) { - // This byte is within the stack memory - backgroundColor = stackMemoryBackgroundColor; - asciiTextColor = standardTextColor; - - } else if (this->settings.highlightFocusedMemory && this->focusedMemoryRegion != nullptr) { - // This byte is within a focused region - backgroundColor = focusedRegionBackgroundColor; + this->setSelected(false); } - const auto* hoveredByteItem = this->hoveredByteItem.value_or(nullptr); - const auto* hoveredAnnotationItem = this->hoveredAnnotationItem.value_or(nullptr); - if (hoveredByteItem != nullptr) { - if (hoveredByteItem == this) { - if (backgroundColor.has_value()) { - backgroundColor->setAlpha(255); + void ByteItem::setValue(unsigned char value) { + this->valueChanged = this->valueInitialised && this->value != value; - } else { - backgroundColor = hoveredBackgroundColor; - } + this->value = value; + this->hexValue = QString::number(this->value, 16).rightJustified(2, '0').toUpper(); + this->asciiValue = (this->value >= 32 && this->value <= 126) + ? std::optional("'" + QString(QChar(this->value)) + "'") : std::nullopt; - } else if (this->settings.highlightHoveredRowAndCol - && ( - hoveredByteItem->currentColumnIndex == this->currentColumnIndex - || hoveredByteItem->currentRowIndex == this->currentRowIndex - ) + this->valueInitialised = this->excludedMemoryRegion == nullptr; + this->update(); + } + + void ByteItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { + painter->setRenderHints( + QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, + true + ); + painter->setPen(Qt::PenStyle::NoPen); + + // TODO: This code could do with some tidying. It's getting quite messy. + + static const auto widgetRect = this->boundingRect(); + static const auto standardTextColor = QColor(0xAF, 0xB1, 0xB3); + static const auto valueChangedTextColor = QColor(0x54, 0x7F, 0xBA); + static auto font = QFont("'Ubuntu', sans-serif"); + + static const auto highlightedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255); + static const auto focusedRegionBackgroundColor = QColor(0x44, 0x44, 0x41, 255); + static const auto stackMemoryBackgroundColor = QColor(0x67, 0x57, 0x20, 210); + static const auto hoveredBackgroundColor = QColor(0x8E, 0x8B, 0x83, 70); + static const auto hoveredNeighbourBackgroundColor = QColor(0x8E, 0x8B, 0x83, 30); + static const auto hoveredAnnotationBackgroundColor = QColor(0x8E, 0x8B, 0x83, 50); + + const auto isEnabled = this->isEnabled(); + + const auto highlightingEnabled = !this->highlightedAddresses.empty(); + const auto highlightedByte = highlightingEnabled && this->highlightedAddresses.contains(this->address); + + auto textColor = standardTextColor; + auto asciiTextColor = QColor(0xA7, 0x77, 0x26); + + auto backgroundColor = std::optional(); + + font.setPixelSize(11); + painter->setFont(font); + + if (highlightedByte) { + backgroundColor = highlightedBackgroundColor; + asciiTextColor = standardTextColor; + + } else if (this->settings.highlightStackMemory && this->currentStackPointer.has_value() + && this->address > this->currentStackPointer ) { - if (backgroundColor.has_value()) { - backgroundColor->setAlpha(220); + // This byte is within the stack memory + backgroundColor = stackMemoryBackgroundColor; + asciiTextColor = standardTextColor; + + } else if (this->settings.highlightFocusedMemory && this->focusedMemoryRegion != nullptr) { + // This byte is within a focused region + backgroundColor = focusedRegionBackgroundColor; + } + + const auto* hoveredByteItem = this->hoveredByteItem.value_or(nullptr); + const auto* hoveredAnnotationItem = this->hoveredAnnotationItem.value_or(nullptr); + if (hoveredByteItem != nullptr) { + if (hoveredByteItem == this) { + if (backgroundColor.has_value()) { + backgroundColor->setAlpha(255); + + } else { + backgroundColor = hoveredBackgroundColor; + } + + } else if (this->settings.highlightHoveredRowAndCol + && ( + hoveredByteItem->currentColumnIndex == this->currentColumnIndex + || hoveredByteItem->currentRowIndex == this->currentRowIndex + ) + ) { + if (backgroundColor.has_value()) { + backgroundColor->setAlpha(220); + + } else { + backgroundColor = hoveredNeighbourBackgroundColor; + } + } + + } else if ( + !this->settings.highlightFocusedMemory + && hoveredAnnotationItem != nullptr + && this->address >= hoveredAnnotationItem->startAddress + && this->address <= hoveredAnnotationItem->endAddress + ) { + backgroundColor = hoveredAnnotationBackgroundColor; + } + + if (backgroundColor.has_value()) { + if (!isEnabled || (highlightingEnabled && !highlightedByte)) { + backgroundColor->setAlpha(100); + } + + painter->setBrush(backgroundColor.value()); + painter->drawRect(widgetRect); + } + + if (this->valueInitialised && this->excludedMemoryRegion == nullptr) { + if (this->settings.displayAsciiValues && this->asciiValue.has_value()) { + if (!isEnabled || (highlightingEnabled && !highlightedByte)) { + asciiTextColor.setAlpha(100); + } + + painter->setPen(asciiTextColor); + painter->drawText(widgetRect, Qt::AlignCenter, this->asciiValue.value()); } else { - backgroundColor = hoveredNeighbourBackgroundColor; + if (!isEnabled || (highlightingEnabled && !highlightedByte) || this->settings.displayAsciiValues) { + textColor.setAlpha(100); + } + + painter->setPen(textColor); + painter->drawText(widgetRect, Qt::AlignCenter, this->hexValue); + return; } - } - - } else if ( - !this->settings.highlightFocusedMemory - && hoveredAnnotationItem != nullptr - && this->address >= hoveredAnnotationItem->startAddress - && this->address <= hoveredAnnotationItem->endAddress - ) { - backgroundColor = hoveredAnnotationBackgroundColor; - } - - if (backgroundColor.has_value()) { - if (!isEnabled || (highlightingEnabled && !highlightedByte)) { - backgroundColor->setAlpha(100); - } - - painter->setBrush(backgroundColor.value()); - painter->drawRect(widgetRect); - } - - if (this->valueInitialised && this->excludedMemoryRegion == nullptr) { - if (this->settings.displayAsciiValues && this->asciiValue.has_value()) { - if (!isEnabled || (highlightingEnabled && !highlightedByte)) { - asciiTextColor.setAlpha(100); - } - - painter->setPen(asciiTextColor); - painter->drawText(widgetRect, Qt::AlignCenter, this->asciiValue.value()); } else { - if (!isEnabled || (highlightingEnabled && !highlightedByte) || this->settings.displayAsciiValues) { - textColor.setAlpha(100); - } - + textColor.setAlpha(100); painter->setPen(textColor); - painter->drawText(widgetRect, Qt::AlignCenter, this->hexValue); - return; + + static const auto placeholderString = QString("??"); + painter->drawText(widgetRect, Qt::AlignCenter, placeholderString); } - - } else { - textColor.setAlpha(100); - painter->setPen(textColor); - - static const auto placeholderString = QString("??"); - painter->drawText(widgetRect, Qt::AlignCenter, placeholderString); } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.cpp index e3a902c4..527cc5c5 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.cpp @@ -1,53 +1,54 @@ #include "ByteItemContainerGraphicsView.hpp" -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + using Bloom::Targets::TargetMemoryDescriptor; -using Bloom::Targets::TargetMemoryDescriptor; + ByteItemContainerGraphicsView::ByteItemContainerGraphicsView( + const TargetMemoryDescriptor& targetMemoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, + InsightWorker& insightWorker, + const HexViewerWidgetSettings& settings, + QLabel* hoveredAddressLabel, + QWidget* parent + ): QGraphicsView(parent) { + this->setObjectName("graphics-view"); + this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); + this->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); + this->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + this->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate); -ByteItemContainerGraphicsView::ByteItemContainerGraphicsView( - const TargetMemoryDescriptor& targetMemoryDescriptor, - std::vector& focusedMemoryRegions, - std::vector& excludedMemoryRegions, - InsightWorker& insightWorker, - const HexViewerWidgetSettings& settings, - QLabel* hoveredAddressLabel, - QWidget* parent -): QGraphicsView(parent) { - this->setObjectName("graphics-view"); - this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); - this->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); - this->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); - this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - this->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate); + this->scene = new ByteItemGraphicsScene( + targetMemoryDescriptor, + focusedMemoryRegions, + excludedMemoryRegions, + insightWorker, + settings, + hoveredAddressLabel, + this + ); - this->scene = new ByteItemGraphicsScene( - targetMemoryDescriptor, - focusedMemoryRegions, - excludedMemoryRegions, - insightWorker, - settings, - hoveredAddressLabel, - this - ); + this->setScene(this->scene); - this->setScene(this->scene); - -} - -void ByteItemContainerGraphicsView::scrollToByteItemAtAddress(std::uint32_t address) { - this->centerOn(this->scene->getByteItemPositionByAddress(address)); -} - -bool ByteItemContainerGraphicsView::event(QEvent* event) { - const auto eventType = event->type(); - if (eventType == QEvent::Type::EnabledChange) { - this->scene->setEnabled(this->isEnabled()); } - return QGraphicsView::event(event); -} + void ByteItemContainerGraphicsView::scrollToByteItemAtAddress(std::uint32_t address) { + this->centerOn(this->scene->getByteItemPositionByAddress(address)); + } -void ByteItemContainerGraphicsView::resizeEvent(QResizeEvent* event) { - QGraphicsView::resizeEvent(event); - this->scene->adjustSize(); + bool ByteItemContainerGraphicsView::event(QEvent* event) { + const auto eventType = event->type(); + if (eventType == QEvent::Type::EnabledChange) { + this->scene->setEnabled(this->isEnabled()); + } + + return QGraphicsView::event(event); + } + + void ByteItemContainerGraphicsView::resizeEvent(QResizeEvent* event) { + QGraphicsView::resizeEvent(event); + this->scene->adjustSize(); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp index 636e71f1..352e4db3 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp @@ -2,494 +2,497 @@ #include -using namespace Bloom::Widgets; - -using Bloom::Targets::TargetMemoryDescriptor; - -ByteItemGraphicsScene::ByteItemGraphicsScene( - const TargetMemoryDescriptor& targetMemoryDescriptor, - std::vector& focusedMemoryRegions, - std::vector& excludedMemoryRegions, - InsightWorker& insightWorker, - const HexViewerWidgetSettings& settings, - QLabel* hoveredAddressLabel, - QGraphicsView* parent -): - QGraphicsScene(parent), - targetMemoryDescriptor(targetMemoryDescriptor), - focusedMemoryRegions(focusedMemoryRegions), - excludedMemoryRegions(excludedMemoryRegions), - insightWorker(insightWorker), - settings(settings), - hoveredAddressLabel(hoveredAddressLabel), - parent(parent) +namespace Bloom::Widgets { - this->setObjectName("byte-widget-container"); + using Bloom::Targets::TargetMemoryDescriptor; - this->byteAddressContainer = new ByteAddressContainer(); - this->addItem(this->byteAddressContainer); + ByteItemGraphicsScene::ByteItemGraphicsScene( + const TargetMemoryDescriptor& targetMemoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, + InsightWorker& insightWorker, + const HexViewerWidgetSettings& settings, + QLabel* hoveredAddressLabel, + QGraphicsView* parent + ) + : QGraphicsScene(parent) + , targetMemoryDescriptor(targetMemoryDescriptor) + , focusedMemoryRegions(focusedMemoryRegions) + , excludedMemoryRegions(excludedMemoryRegions) + , insightWorker(insightWorker) + , settings(settings) + , hoveredAddressLabel(hoveredAddressLabel) + , parent(parent) + { + this->setObjectName("byte-widget-container"); - // Construct ByteWidget objects - const auto memorySize = this->targetMemoryDescriptor.size(); - const auto startAddress = this->targetMemoryDescriptor.addressRange.startAddress; - for (std::uint32_t i = 0; i < memorySize; i++) { - const auto address = startAddress + i; + this->byteAddressContainer = new ByteAddressContainer(); + this->addItem(this->byteAddressContainer); - auto* byteWidget = new ByteItem( - i, - address, - this->currentStackPointer, - this->hoveredByteWidget, - this->hoveredAnnotationItem, - this->highlightedAddresses, - settings + // Construct ByteWidget objects + const auto memorySize = this->targetMemoryDescriptor.size(); + const auto startAddress = this->targetMemoryDescriptor.addressRange.startAddress; + for (std::uint32_t i = 0; i < memorySize; i++) { + const auto address = startAddress + i; + + auto* byteWidget = new ByteItem( + i, + address, + this->currentStackPointer, + this->hoveredByteWidget, + this->hoveredAnnotationItem, + this->highlightedAddresses, + settings + ); + + this->byteItemsByAddress.insert(std::pair( + address, + byteWidget + )); + + this->addItem(byteWidget); + } + + QObject::connect( + &insightWorker, + &InsightWorker::targetStateUpdated, + this, + &ByteItemGraphicsScene::onTargetStateChanged ); - this->byteItemsByAddress.insert(std::pair( - address, - byteWidget - )); - - this->addItem(byteWidget); + this->refreshRegions(); + this->adjustSize(); } - QObject::connect( - &insightWorker, - &InsightWorker::targetStateUpdated, - this, - &ByteItemGraphicsScene::onTargetStateChanged - ); + void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buffer) { + for (auto& [address, byteWidget] : this->byteItemsByAddress) { + byteWidget->setValue(buffer.at(byteWidget->byteIndex)); + } - this->refreshRegions(); - this->adjustSize(); -} - -void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buffer) { - for (auto& [address, byteWidget] : this->byteItemsByAddress) { - byteWidget->setValue(buffer.at(byteWidget->byteIndex)); + this->updateAnnotationValues(buffer); + this->lastValueBuffer = buffer; } - this->updateAnnotationValues(buffer); - this->lastValueBuffer = buffer; -} + void ByteItemGraphicsScene::updateStackPointer(std::uint32_t stackPointer) { + this->currentStackPointer = stackPointer; + this->invalidateChildItemCaches(); + } -void ByteItemGraphicsScene::updateStackPointer(std::uint32_t stackPointer) { - this->currentStackPointer = stackPointer; - this->invalidateChildItemCaches(); -} + void ByteItemGraphicsScene::setHighlightedAddresses(const std::set& highlightedAddresses) { + this->highlightedAddresses = highlightedAddresses; + this->invalidateChildItemCaches(); + } -void ByteItemGraphicsScene::setHighlightedAddresses(const std::set& highlightedAddresses) { - this->highlightedAddresses = highlightedAddresses; - this->invalidateChildItemCaches(); -} + void ByteItemGraphicsScene::refreshRegions() { + for (auto& [byteAddress, byteWidget] : this->byteItemsByAddress) { + byteWidget->focusedMemoryRegion = nullptr; + byteWidget->excludedMemoryRegion = nullptr; -void ByteItemGraphicsScene::refreshRegions() { - for (auto& [byteAddress, byteWidget] : this->byteItemsByAddress) { - byteWidget->focusedMemoryRegion = nullptr; - byteWidget->excludedMemoryRegion = nullptr; + for (const auto& focusedRegion : this->focusedMemoryRegions) { + if (byteAddress >= focusedRegion.addressRange.startAddress + && byteAddress <= focusedRegion.addressRange.endAddress + ) { + byteWidget->focusedMemoryRegion = &focusedRegion; + break; + } + } + + for (const auto& excludedRegion : this->excludedMemoryRegions) { + if (byteAddress >= excludedRegion.addressRange.startAddress + && byteAddress <= excludedRegion.addressRange.endAddress + ) { + byteWidget->excludedMemoryRegion = &excludedRegion; + break; + } + } + + byteWidget->update(); + } + + // Refresh annotation items + this->hoveredAnnotationItem = std::nullopt; + for (auto* annotationItem : this->annotationItems) { + this->removeItem(annotationItem); + delete annotationItem; + } + + this->annotationItems.clear(); + this->valueAnnotationItems.clear(); for (const auto& focusedRegion : this->focusedMemoryRegions) { - if (byteAddress >= focusedRegion.addressRange.startAddress - && byteAddress <= focusedRegion.addressRange.endAddress - ) { - byteWidget->focusedMemoryRegion = &focusedRegion; - break; + auto* annotationItem = new AnnotationItem(focusedRegion, AnnotationItemPosition::BOTTOM); + annotationItem->setEnabled(this->enabled); + this->addItem(annotationItem); + this->annotationItems.emplace_back(annotationItem); + + if (focusedRegion.dataType != MemoryRegionDataType::UNKNOWN) { + auto* valueAnnotationItem = new ValueAnnotationItem(focusedRegion); + valueAnnotationItem->setEnabled(this->enabled); + this->addItem(valueAnnotationItem); + this->annotationItems.emplace_back(valueAnnotationItem); + this->valueAnnotationItems.emplace_back(valueAnnotationItem); } } - for (const auto& excludedRegion : this->excludedMemoryRegions) { - if (byteAddress >= excludedRegion.addressRange.startAddress - && byteAddress <= excludedRegion.addressRange.endAddress - ) { - byteWidget->excludedMemoryRegion = &excludedRegion; - break; + if (this->targetState == Targets::TargetState::STOPPED && this->enabled && !this->lastValueBuffer.empty()) { + this->updateAnnotationValues(this->lastValueBuffer); + } + + this->adjustSize(true); + } + + void ByteItemGraphicsScene::adjustSize(bool forced) { + const auto width = this->getSceneWidth(); + + const auto columnCount = static_cast( + std::floor( + (width - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + + ByteItem::RIGHT_MARGIN) / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + ) + ); + const auto rowCount = static_cast( + std::ceil(static_cast(this->byteItemsByAddress.size()) / static_cast(columnCount)) + ); + + // Don't bother recalculating the byte & annotation positions if the number of rows & columns haven't changed. + if (this->byteItemsByAddress.empty() + || ( + !forced + && rowCount == this->byteItemsByRowIndex.size() + && columnCount == this->byteItemsByColumnIndex.size() + ) + ) { + this->setSceneRect( + 0, + 0, + width, + std::max(static_cast(this->sceneRect().height()), this->parent->viewport()->height()) + ); + + return; + } + + if (!this->byteItemsByAddress.empty()) { + this->adjustByteItemPositions(); + + if (this->settings.displayAnnotations) { + this->adjustAnnotationItemPositions(); } + + const auto* lastByteItem = (--this->byteItemsByAddress.end())->second; + const auto sceneHeight = static_cast( + lastByteItem->pos().y() + ByteItem::HEIGHT + AnnotationItem::BOTTOM_HEIGHT + this->margins.bottom() + ); + + this->setSceneRect( + 0, + 0, + width, + std::max(sceneHeight, this->parent->height()) + ); } - byteWidget->update(); + this->update(); } - // Refresh annotation items - this->hoveredAnnotationItem = std::nullopt; - for (auto* annotationItem : this->annotationItems) { - this->removeItem(annotationItem); - delete annotationItem; - } + void ByteItemGraphicsScene::setEnabled(bool enabled) { + if (this->enabled != enabled) { + this->enabled = enabled; - this->annotationItems.clear(); - this->valueAnnotationItems.clear(); + for (auto& [byteAddress, byteItem] : this->byteItemsByAddress) { + byteItem->setEnabled(this->enabled); + } - for (const auto& focusedRegion : this->focusedMemoryRegions) { - auto* annotationItem = new AnnotationItem(focusedRegion, AnnotationItemPosition::BOTTOM); - annotationItem->setEnabled(this->enabled); - this->addItem(annotationItem); - this->annotationItems.emplace_back(annotationItem); + for (auto* annotationItem : this->annotationItems) { + annotationItem->setEnabled(this->enabled); + } - if (focusedRegion.dataType != MemoryRegionDataType::UNKNOWN) { - auto* valueAnnotationItem = new ValueAnnotationItem(focusedRegion); - valueAnnotationItem->setEnabled(this->enabled); - this->addItem(valueAnnotationItem); - this->annotationItems.emplace_back(valueAnnotationItem); - this->valueAnnotationItems.emplace_back(valueAnnotationItem); + this->byteAddressContainer->setEnabled(enabled); + this->byteAddressContainer->update(); + this->update(); } } - if (this->targetState == Targets::TargetState::STOPPED && this->enabled && !this->lastValueBuffer.empty()) { - this->updateAnnotationValues(this->lastValueBuffer); - } - - this->adjustSize(true); -} - -void ByteItemGraphicsScene::adjustSize(bool forced) { - const auto width = this->getSceneWidth(); - - const auto columnCount = static_cast( - std::floor( - (width - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + ByteItem::RIGHT_MARGIN) - / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) - ) - ); - const auto rowCount = static_cast( - std::ceil(static_cast(this->byteItemsByAddress.size()) / static_cast(columnCount)) - ); - - // Don't bother recalculating the byte item & annotation positions if the number of rows & columns have not changed. - if (this->byteItemsByAddress.empty() - || ( - !forced - && rowCount == this->byteItemsByRowIndex.size() - && columnCount == this->byteItemsByColumnIndex.size() - ) - ) { - this->setSceneRect( - 0, - 0, - width, - std::max(static_cast(this->sceneRect().height()), this->parent->viewport()->height()) - ); - - return; - } - - if (!this->byteItemsByAddress.empty()) { - this->adjustByteItemPositions(); - - if (this->settings.displayAnnotations) { - this->adjustAnnotationItemPositions(); - } - - const auto* lastByteItem = (--this->byteItemsByAddress.end())->second; - const auto sceneHeight = static_cast( - lastByteItem->pos().y() + ByteItem::HEIGHT + AnnotationItem::BOTTOM_HEIGHT + this->margins.bottom() - ); - - this->setSceneRect( - 0, - 0, - width, - std::max(sceneHeight, this->parent->height()) - ); - } - - this->update(); -} - -void ByteItemGraphicsScene::setEnabled(bool enabled) { - if (this->enabled != enabled) { - this->enabled = enabled; - - for (auto& [byteAddress, byteItem] : this->byteItemsByAddress) { - byteItem->setEnabled(this->enabled); + void ByteItemGraphicsScene::invalidateChildItemCaches() { + for (auto& [address, byteWidget] : this->byteItemsByAddress) { + byteWidget->update(); } for (auto* annotationItem : this->annotationItems) { - annotationItem->setEnabled(this->enabled); + annotationItem->update(); + } + } + + QPointF ByteItemGraphicsScene::getByteItemPositionByAddress(std::uint32_t address) { + if (this->byteItemsByAddress.contains(address)) { + return this->byteItemsByAddress.at(address)->pos(); } - this->byteAddressContainer->setEnabled(enabled); - this->byteAddressContainer->update(); - this->update(); - } -} - -void ByteItemGraphicsScene::invalidateChildItemCaches() { - for (auto& [address, byteWidget] : this->byteItemsByAddress) { - byteWidget->update(); + return QPointF(); } - for (auto* annotationItem : this->annotationItems) { - annotationItem->update(); - } -} - -QPointF ByteItemGraphicsScene::getByteItemPositionByAddress(std::uint32_t address) { - if (this->byteItemsByAddress.contains(address)) { - return this->byteItemsByAddress.at(address)->pos(); - } - - return QPointF(); -} - -bool ByteItemGraphicsScene::event(QEvent* event) { - if (event->type() == QEvent::Type::GraphicsSceneLeave && this->hoveredByteWidget.has_value()) { - this->onByteWidgetLeave(); - } - - return QGraphicsScene::event(event); -} - -void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) { - auto hoveredItems = this->items(mouseEvent->scenePos()); - ByteItem* hoveredByteItem = nullptr; - AnnotationItem* hoveredAnnotationItem = nullptr; - - if (!hoveredItems.empty()) { - hoveredByteItem = dynamic_cast(hoveredItems.at(0)); - hoveredAnnotationItem = dynamic_cast(hoveredItems.at(0)); - } - - if (hoveredByteItem != nullptr) { - this->onByteWidgetEnter(hoveredByteItem); - return; - } - - if (this->hoveredByteWidget.has_value()) { - this->onByteWidgetLeave(); - } - - if (hoveredAnnotationItem != nullptr) { - this->onAnnotationItemEnter(hoveredAnnotationItem); - return; - } - - if (this->hoveredAnnotationItem.has_value()) { - this->onAnnotationItemLeave(); - } -} - -void ByteItemGraphicsScene::updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer) { - const auto memoryStartAddress = this->targetMemoryDescriptor.addressRange.startAddress; - for (auto* valueAnnotationItem : this->valueAnnotationItems) { - if (valueAnnotationItem->size > buffer.size()) { - continue; + bool ByteItemGraphicsScene::event(QEvent* event) { + if (event->type() == QEvent::Type::GraphicsSceneLeave && this->hoveredByteWidget.has_value()) { + this->onByteWidgetLeave(); } - const auto relativeStartAddress = valueAnnotationItem->startAddress - memoryStartAddress; - const auto relativeEndAddress = valueAnnotationItem->endAddress - memoryStartAddress; - - valueAnnotationItem->setValue(Targets::TargetMemoryBuffer( - buffer.begin() + relativeStartAddress, - buffer.begin() + relativeEndAddress + 1 - )); + return QGraphicsScene::event(event); } -} -void ByteItemGraphicsScene::adjustByteItemPositions() { - const auto columnCount = static_cast( - std::floor( - (this->getSceneWidth() - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + ByteItem::RIGHT_MARGIN) - / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) - ) - ); + void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) { + auto hoveredItems = this->items(mouseEvent->scenePos()); + ByteItem* hoveredByteItem = nullptr; + AnnotationItem* hoveredAnnotationItem = nullptr; - std::map> byteWidgetsByRowIndex; - std::map> byteWidgetsByColumnIndex; + if (!hoveredItems.empty()) { + hoveredByteItem = dynamic_cast(hoveredItems.at(0)); + hoveredAnnotationItem = dynamic_cast(hoveredItems.at(0)); + } - auto rowIndicesWithTopAnnotations = std::set(); - auto rowIndicesWithBottomAnnotations = std::set(); - const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange; + if (hoveredByteItem != nullptr) { + this->onByteWidgetEnter(hoveredByteItem); + return; + } - for (auto* annotationItem : this->annotationItems) { - const auto firstByteRowIndex = static_cast( - std::ceil(static_cast((annotationItem->startAddress - memoryAddressRange.startAddress) + 1) - / static_cast(columnCount)) - 1 + if (this->hoveredByteWidget.has_value()) { + this->onByteWidgetLeave(); + } + + if (hoveredAnnotationItem != nullptr) { + this->onAnnotationItemEnter(hoveredAnnotationItem); + return; + } + + if (this->hoveredAnnotationItem.has_value()) { + this->onAnnotationItemLeave(); + } + } + + void ByteItemGraphicsScene::updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer) { + const auto memoryStartAddress = this->targetMemoryDescriptor.addressRange.startAddress; + for (auto* valueAnnotationItem : this->valueAnnotationItems) { + if (valueAnnotationItem->size > buffer.size()) { + continue; + } + + const auto relativeStartAddress = valueAnnotationItem->startAddress - memoryStartAddress; + const auto relativeEndAddress = valueAnnotationItem->endAddress - memoryStartAddress; + + valueAnnotationItem->setValue(Targets::TargetMemoryBuffer( + buffer.begin() + relativeStartAddress, + buffer.begin() + relativeEndAddress + 1 + )); + } + } + + void ByteItemGraphicsScene::adjustByteItemPositions() { + const auto columnCount = static_cast( + std::floor( + (this->getSceneWidth() - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + + ByteItem::RIGHT_MARGIN) / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + ) ); - const auto lastByteRowIndex = static_cast( - std::ceil(static_cast((annotationItem->endAddress - memoryAddressRange.startAddress) + 1) - / static_cast(columnCount)) - 1 - ); + std::map> byteWidgetsByRowIndex; + std::map> byteWidgetsByColumnIndex; - // We only display annotations that span a single row. - if (this->settings.displayAnnotations && firstByteRowIndex == lastByteRowIndex) { - annotationItem->show(); + auto rowIndicesWithTopAnnotations = std::set(); + auto rowIndicesWithBottomAnnotations = std::set(); + const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange; + + for (auto* annotationItem : this->annotationItems) { + const auto firstByteRowIndex = static_cast( + std::ceil(static_cast((annotationItem->startAddress - memoryAddressRange.startAddress) + 1) + / static_cast(columnCount)) - 1 + ); + + const auto lastByteRowIndex = static_cast( + std::ceil(static_cast((annotationItem->endAddress - memoryAddressRange.startAddress) + 1) + / static_cast(columnCount)) - 1 + ); + + // We only display annotations that span a single row. + if (this->settings.displayAnnotations && firstByteRowIndex == lastByteRowIndex) { + annotationItem->show(); + + if (annotationItem->position == AnnotationItemPosition::TOP) { + rowIndicesWithTopAnnotations.insert(firstByteRowIndex); + + } else if (annotationItem->position == AnnotationItemPosition::BOTTOM) { + rowIndicesWithBottomAnnotations.insert(firstByteRowIndex); + } + + } else { + annotationItem->hide(); + } + } + + constexpr auto annotationTopHeight = AnnotationItem::TOP_HEIGHT; + constexpr auto annotationBottomHeight = AnnotationItem::BOTTOM_HEIGHT; + + std::size_t lastRowIndex = 0; + int rowYPosition = margins.top(); + auto currentRowAnnotatedTop = false; + + for (auto& [address, byteWidget] : this->byteItemsByAddress) { + const auto rowIndex = static_cast( + std::ceil(static_cast(byteWidget->byteIndex + 1) / static_cast(columnCount)) - 1 + ); + const auto columnIndex = static_cast( + static_cast(byteWidget->byteIndex) + - (std::floor(byteWidget->byteIndex / columnCount) * static_cast(columnCount)) + ); + + if (rowIndex != lastRowIndex) { + rowYPosition += ByteItem::HEIGHT + ByteItem::BOTTOM_MARGIN; + currentRowAnnotatedTop = false; + + if (rowIndicesWithBottomAnnotations.contains(lastRowIndex)) { + rowYPosition += annotationBottomHeight; + } + } + + if (!currentRowAnnotatedTop && rowIndicesWithTopAnnotations.contains(rowIndex)) { + rowYPosition += annotationTopHeight; + currentRowAnnotatedTop = true; + } + + byteWidget->setPos( + static_cast( + columnIndex * (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + this->margins.left() + + ByteAddressContainer::WIDTH + ), + rowYPosition + ); + + byteWidget->currentRowIndex = rowIndex; + byteWidget->currentColumnIndex = columnIndex; + + byteWidgetsByRowIndex[byteWidget->currentRowIndex].emplace_back(byteWidget); + byteWidgetsByColumnIndex[byteWidget->currentColumnIndex].emplace_back(byteWidget); + + lastRowIndex = rowIndex; + } + + this->byteItemsByRowIndex = std::move(byteWidgetsByRowIndex); + this->byteItemsByColumnIndex = std::move(byteWidgetsByColumnIndex); + + this->byteAddressContainer->adjustAddressLabels(this->byteItemsByRowIndex); + } + + void ByteItemGraphicsScene::adjustAnnotationItemPositions() { + if (this->byteItemsByAddress.empty() || !this->settings.displayAnnotations) { + return; + } + + for (auto* annotationItem : this->annotationItems) { + if (!this->byteItemsByAddress.contains(annotationItem->startAddress)) { + annotationItem->hide(); + continue; + } + + const auto firstByteItemPosition = this->byteItemsByAddress.at(annotationItem->startAddress)->pos(); if (annotationItem->position == AnnotationItemPosition::TOP) { - rowIndicesWithTopAnnotations.insert(firstByteRowIndex); + annotationItem->setPos( + firstByteItemPosition.x(), + firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT - 1 + ); } else if (annotationItem->position == AnnotationItemPosition::BOTTOM) { - rowIndicesWithBottomAnnotations.insert(firstByteRowIndex); + annotationItem->setPos( + firstByteItemPosition.x(), + firstByteItemPosition.y() + ByteItem::HEIGHT + ); + } + } + } + + void ByteItemGraphicsScene::onTargetStateChanged(Targets::TargetState newState) { + using Targets::TargetState; + this->targetState = newState; + } + + void ByteItemGraphicsScene::onByteWidgetEnter(ByteItem* widget) { + if (this->hoveredByteWidget.has_value()) { + if (this->hoveredByteWidget.value() == widget) { + // This byte item is already marked as hovered + return; + } + + this->onByteWidgetLeave(); + } + + this->hoveredByteWidget = widget; + + this->hoveredAddressLabel->setText( + "Relative Address (Absolute Address): " + widget->relativeAddressHex + " (" + widget->addressHex + ")" + ); + + if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) { + for (auto& byteWidget : this->byteItemsByColumnIndex.at(widget->currentColumnIndex)) { + byteWidget->update(); + } + + for (auto& byteWidget : this->byteItemsByRowIndex.at(widget->currentRowIndex)) { + byteWidget->update(); } } else { - annotationItem->hide(); + widget->update(); } } - constexpr auto annotationTopHeight = AnnotationItem::TOP_HEIGHT; - constexpr auto annotationBottomHeight = AnnotationItem::BOTTOM_HEIGHT; + void ByteItemGraphicsScene::onByteWidgetLeave() { + auto* byteItem = this->hoveredByteWidget.value(); + this->hoveredByteWidget = std::nullopt; - std::size_t lastRowIndex = 0; - int rowYPosition = margins.top(); - auto currentRowAnnotatedTop = false; + this->hoveredAddressLabel->setText("Relative Address (Absolute Address):"); - for (auto& [address, byteWidget] : this->byteItemsByAddress) { - const auto rowIndex = static_cast( - std::ceil(static_cast(byteWidget->byteIndex + 1) / static_cast(columnCount)) - 1 - ); - const auto columnIndex = static_cast( - static_cast(byteWidget->byteIndex) - - (std::floor(byteWidget->byteIndex / columnCount) * static_cast(columnCount)) - ); - - if (rowIndex != lastRowIndex) { - rowYPosition += ByteItem::HEIGHT + ByteItem::BOTTOM_MARGIN; - currentRowAnnotatedTop = false; - - if (rowIndicesWithBottomAnnotations.contains(lastRowIndex)) { - rowYPosition += annotationBottomHeight; + if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) { + for (auto& byteWidget : this->byteItemsByColumnIndex.at(byteItem->currentColumnIndex)) { + byteWidget->update(); } + + for (auto& byteWidget : this->byteItemsByRowIndex.at(byteItem->currentRowIndex)) { + byteWidget->update(); + } + + } else { + byteItem->update(); } - - if (!currentRowAnnotatedTop && rowIndicesWithTopAnnotations.contains(rowIndex)) { - rowYPosition += annotationTopHeight; - currentRowAnnotatedTop = true; - } - - byteWidget->setPos( - static_cast( - columnIndex * (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + this->margins.left() + ByteAddressContainer::WIDTH), - rowYPosition - ); - - byteWidget->currentRowIndex = rowIndex; - byteWidget->currentColumnIndex = columnIndex; - - byteWidgetsByRowIndex[byteWidget->currentRowIndex].emplace_back(byteWidget); - byteWidgetsByColumnIndex[byteWidget->currentColumnIndex].emplace_back(byteWidget); - - lastRowIndex = rowIndex; } - this->byteItemsByRowIndex = std::move(byteWidgetsByRowIndex); - this->byteItemsByColumnIndex = std::move(byteWidgetsByColumnIndex); + void ByteItemGraphicsScene::onAnnotationItemEnter(AnnotationItem* annotationItem) { + if (this->hoveredAnnotationItem.has_value()) { + if (this->hoveredAnnotationItem.value() == annotationItem) { + return; + } - this->byteAddressContainer->adjustAddressLabels(this->byteItemsByRowIndex); -} - -void ByteItemGraphicsScene::adjustAnnotationItemPositions() { - if (this->byteItemsByAddress.empty() || !this->settings.displayAnnotations) { - return; - } - - for (auto* annotationItem : this->annotationItems) { - if (!this->byteItemsByAddress.contains(annotationItem->startAddress)) { - annotationItem->hide(); - continue; + this->onAnnotationItemLeave(); } - const auto firstByteItemPosition = this->byteItemsByAddress.at(annotationItem->startAddress)->pos(); + this->hoveredAnnotationItem = annotationItem; - if (annotationItem->position == AnnotationItemPosition::TOP) { - annotationItem->setPos( - firstByteItemPosition.x(), - firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT - 1 - ); + for ( + auto byteItemAddress = annotationItem->startAddress; + byteItemAddress <= annotationItem->endAddress; + byteItemAddress++ + ) { + this->byteItemsByAddress.at(byteItemAddress)->update(); + } + } - } else if (annotationItem->position == AnnotationItemPosition::BOTTOM) { - annotationItem->setPos( - firstByteItemPosition.x(), - firstByteItemPosition.y() + ByteItem::HEIGHT - ); + void ByteItemGraphicsScene::onAnnotationItemLeave() { + auto* annotationItem = this->hoveredAnnotationItem.value(); + this->hoveredAnnotationItem = std::nullopt; + + for ( + auto byteItemAddress = annotationItem->startAddress; + byteItemAddress <= annotationItem->endAddress; + byteItemAddress++ + ) { + this->byteItemsByAddress.at(byteItemAddress)->update(); } } } - -void ByteItemGraphicsScene::onTargetStateChanged(Targets::TargetState newState) { - using Targets::TargetState; - this->targetState = newState; -} - -void ByteItemGraphicsScene::onByteWidgetEnter(ByteItem* widget) { - if (this->hoveredByteWidget.has_value()) { - if (this->hoveredByteWidget.value() == widget) { - // This byte item is already marked as hovered - return; - } - - this->onByteWidgetLeave(); - } - - this->hoveredByteWidget = widget; - - this->hoveredAddressLabel->setText( - "Relative Address (Absolute Address): " + widget->relativeAddressHex + " (" + widget->addressHex + ")" - ); - - if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) { - for (auto& byteWidget : this->byteItemsByColumnIndex.at(widget->currentColumnIndex)) { - byteWidget->update(); - } - - for (auto& byteWidget : this->byteItemsByRowIndex.at(widget->currentRowIndex)) { - byteWidget->update(); - } - - } else { - widget->update(); - } -} - -void ByteItemGraphicsScene::onByteWidgetLeave() { - auto* byteItem = this->hoveredByteWidget.value(); - this->hoveredByteWidget = std::nullopt; - - this->hoveredAddressLabel->setText("Relative Address (Absolute Address):"); - - if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) { - for (auto& byteWidget : this->byteItemsByColumnIndex.at(byteItem->currentColumnIndex)) { - byteWidget->update(); - } - - for (auto& byteWidget : this->byteItemsByRowIndex.at(byteItem->currentRowIndex)) { - byteWidget->update(); - } - - } else { - byteItem->update(); - } -} - -void ByteItemGraphicsScene::onAnnotationItemEnter(AnnotationItem* annotationItem) { - if (this->hoveredAnnotationItem.has_value()) { - if (this->hoveredAnnotationItem.value() == annotationItem) { - return; - } - - this->onAnnotationItemLeave(); - } - - this->hoveredAnnotationItem = annotationItem; - - for ( - auto byteItemAddress = annotationItem->startAddress; - byteItemAddress <= annotationItem->endAddress; - byteItemAddress++ - ) { - this->byteItemsByAddress.at(byteItemAddress)->update(); - } -} - -void ByteItemGraphicsScene::onAnnotationItemLeave() { - auto* annotationItem = this->hoveredAnnotationItem.value(); - this->hoveredAnnotationItem = std::nullopt; - - for ( - auto byteItemAddress = annotationItem->startAddress; - byteItemAddress <= annotationItem->endAddress; - byteItemAddress++ - ) { - this->byteItemsByAddress.at(byteItemAddress)->update(); - } -} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp index c9b0e73b..6a22afaa 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp @@ -5,240 +5,244 @@ #include "src/Helpers/Paths.hpp" #include "src/Exceptions/Exception.hpp" -using namespace Bloom::Widgets; -using namespace Bloom::Exceptions; - -using Bloom::Targets::TargetMemoryDescriptor; - -HexViewerWidget::HexViewerWidget( - const TargetMemoryDescriptor& targetMemoryDescriptor, - HexViewerWidgetSettings& settings, - std::vector& focusedMemoryRegions, - std::vector& excludedMemoryRegions, - InsightWorker& insightWorker, - QWidget* parent -): - QWidget(parent), - targetMemoryDescriptor(targetMemoryDescriptor), - settings(settings), - focusedMemoryRegions(focusedMemoryRegions), - excludedMemoryRegions(excludedMemoryRegions), - insightWorker(insightWorker) +namespace Bloom::Widgets { - this->setObjectName("hex-viewer-widget"); - this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + using namespace Bloom::Exceptions; - auto widgetUiFile = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget" - + "/UiFiles/HexViewerWidget.ui" - ) - ); + using Bloom::Targets::TargetMemoryDescriptor; - if (!widgetUiFile.open(QFile::ReadOnly)) { - throw Exception("Failed to open HexViewerWidget UI file"); - } + HexViewerWidget::HexViewerWidget( + const TargetMemoryDescriptor& targetMemoryDescriptor, + HexViewerWidgetSettings& settings, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, + InsightWorker& insightWorker, + QWidget* parent + ) + : QWidget(parent) + , targetMemoryDescriptor(targetMemoryDescriptor) + , settings(settings) + , focusedMemoryRegions(focusedMemoryRegions) + , excludedMemoryRegions(excludedMemoryRegions) + , insightWorker(insightWorker) + { + this->setObjectName("hex-viewer-widget"); + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - auto uiLoader = UiLoader(this); - this->container = uiLoader.load(&widgetUiFile, this); - this->container->setFixedSize(this->size()); - this->container->setContentsMargins(0, 0, 0, 0); + auto widgetUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget" + + "/UiFiles/HexViewerWidget.ui" + ) + ); - this->toolBar = this->container->findChild("tool-bar"); - this->bottomBar = this->container->findChild("bottom-bar"); + if (!widgetUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open HexViewerWidget UI file"); + } - this->refreshButton = this->toolBar->findChild("refresh-memory-btn"); - this->highlightStackMemoryButton = this->toolBar->findChild("highlight-stack-memory-btn"); - this->highlightHoveredRowAndColumnButton = this->toolBar->findChild( - "highlight-hovered-rows-columns-btn" - ); - this->highlightFocusedMemoryButton = this->container->findChild( - "highlight-focused-memory-btn" - ); - this->displayAnnotationsButton = this->container->findChild("display-annotations-btn"); - this->displayAsciiButton = this->container->findChild("display-ascii-btn"); + auto uiLoader = UiLoader(this); + this->container = uiLoader.load(&widgetUiFile, this); + this->container->setFixedSize(this->size()); + this->container->setContentsMargins(0, 0, 0, 0); - this->goToAddressInput = this->container->findChild("go-to-address-input"); + this->toolBar = this->container->findChild("tool-bar"); + this->bottomBar = this->container->findChild("bottom-bar"); - this->toolBar->setContentsMargins(0, 0, 0, 0); - this->toolBar->layout()->setContentsMargins(5, 0, 5, 1); + this->refreshButton = this->toolBar->findChild("refresh-memory-btn"); + this->highlightStackMemoryButton = this->toolBar->findChild( + "highlight-stack-memory-btn" + ); + this->highlightHoveredRowAndColumnButton = this->toolBar->findChild( + "highlight-hovered-rows-columns-btn" + ); + this->highlightFocusedMemoryButton = this->container->findChild( + "highlight-focused-memory-btn" + ); + this->displayAnnotationsButton = this->container->findChild("display-annotations-btn"); + this->displayAsciiButton = this->container->findChild("display-ascii-btn"); - this->bottomBar->setContentsMargins(0, 0, 0, 0); - this->bottomBar->layout()->setContentsMargins(5, 0, 5, 0); + this->goToAddressInput = this->container->findChild("go-to-address-input"); - this->hoveredAddressLabel = this->bottomBar->findChild("byte-address-label"); + this->toolBar->setContentsMargins(0, 0, 0, 0); + this->toolBar->layout()->setContentsMargins(5, 0, 5, 1); - this->byteItemGraphicsViewContainer = this->container->findChild("graphics-view-container"); - this->byteItemGraphicsView = new ByteItemContainerGraphicsView( - this->targetMemoryDescriptor, - this->focusedMemoryRegions, - this->excludedMemoryRegions, - this->insightWorker, - this->settings, - this->hoveredAddressLabel, - this->byteItemGraphicsViewContainer - ); - this->byteItemGraphicsScene = this->byteItemGraphicsView->getScene(); + this->bottomBar->setContentsMargins(0, 0, 0, 0); + this->bottomBar->layout()->setContentsMargins(5, 0, 5, 0); - this->setHoveredRowAndColumnHighlightingEnabled(this->settings.highlightHoveredRowAndCol); - this->setFocusedMemoryHighlightingEnabled(this->settings.highlightFocusedMemory); - this->setAnnotationsEnabled(this->settings.displayAnnotations); - this->setDisplayAsciiEnabled(this->settings.displayAsciiValues); + this->hoveredAddressLabel = this->bottomBar->findChild("byte-address-label"); - if (this->targetMemoryDescriptor.type == Targets::TargetMemoryType::RAM) { - this->highlightStackMemoryButton->show(); - this->setStackMemoryHighlightingEnabled(this->settings.highlightStackMemory); + this->byteItemGraphicsViewContainer = this->container->findChild("graphics-view-container"); + this->byteItemGraphicsView = new ByteItemContainerGraphicsView( + this->targetMemoryDescriptor, + this->focusedMemoryRegions, + this->excludedMemoryRegions, + this->insightWorker, + this->settings, + this->hoveredAddressLabel, + this->byteItemGraphicsViewContainer + ); + this->byteItemGraphicsScene = this->byteItemGraphicsView->getScene(); + + this->setHoveredRowAndColumnHighlightingEnabled(this->settings.highlightHoveredRowAndCol); + this->setFocusedMemoryHighlightingEnabled(this->settings.highlightFocusedMemory); + this->setAnnotationsEnabled(this->settings.displayAnnotations); + this->setDisplayAsciiEnabled(this->settings.displayAsciiValues); + + if (this->targetMemoryDescriptor.type == Targets::TargetMemoryType::RAM) { + this->highlightStackMemoryButton->show(); + this->setStackMemoryHighlightingEnabled(this->settings.highlightStackMemory); + + QObject::connect( + this->highlightStackMemoryButton, + &QToolButton::clicked, + this, + [this] { + this->setStackMemoryHighlightingEnabled(!this->settings.highlightStackMemory); + } + ); + + } else { + this->highlightStackMemoryButton->hide(); + this->setStackMemoryHighlightingEnabled(false); + } QObject::connect( - this->highlightStackMemoryButton, + this->highlightHoveredRowAndColumnButton, &QToolButton::clicked, this, [this] { - this->setStackMemoryHighlightingEnabled(!this->settings.highlightStackMemory); + this->setHoveredRowAndColumnHighlightingEnabled(!this->settings.highlightHoveredRowAndCol); } ); - } else { - this->highlightStackMemoryButton->hide(); - this->setStackMemoryHighlightingEnabled(false); + QObject::connect( + this->highlightFocusedMemoryButton, + &QToolButton::clicked, + this, + [this] { + this->setFocusedMemoryHighlightingEnabled(!this->settings.highlightFocusedMemory); + } + ); + + QObject::connect( + this->displayAnnotationsButton, + &QToolButton::clicked, + this, + [this] { + this->setAnnotationsEnabled(!this->settings.displayAnnotations); + } + ); + + QObject::connect( + this->displayAsciiButton, + &QToolButton::clicked, + this, + [this] { + this->setDisplayAsciiEnabled(!this->settings.displayAsciiValues); + } + ); + + QObject::connect( + this->goToAddressInput, + &QLineEdit::textEdited, + this, + &HexViewerWidget::onGoToAddressInputChanged + ); + + QObject::connect( + this->goToAddressInput, + &TextInput::focusChanged, + this, + &HexViewerWidget::onGoToAddressInputChanged + ); + + QObject::connect( + &insightWorker, + &InsightWorker::targetStateUpdated, + this, + &HexViewerWidget::onTargetStateChanged + ); + + this->show(); } - QObject::connect( - this->highlightHoveredRowAndColumnButton, - &QToolButton::clicked, - this, - [this] { - this->setHoveredRowAndColumnHighlightingEnabled(!this->settings.highlightHoveredRowAndCol); - } - ); - - QObject::connect( - this->highlightFocusedMemoryButton, - &QToolButton::clicked, - this, - [this] { - this->setFocusedMemoryHighlightingEnabled(!this->settings.highlightFocusedMemory); - } - ); - - QObject::connect( - this->displayAnnotationsButton, - &QToolButton::clicked, - this, - [this] { - this->setAnnotationsEnabled(!this->settings.displayAnnotations); - } - ); - - QObject::connect( - this->displayAsciiButton, - &QToolButton::clicked, - this, - [this] { - this->setDisplayAsciiEnabled(!this->settings.displayAsciiValues); - } - ); - - QObject::connect( - this->goToAddressInput, - &QLineEdit::textEdited, - this, - &HexViewerWidget::onGoToAddressInputChanged - ); - - QObject::connect( - this->goToAddressInput, - &TextInput::focusChanged, - this, - &HexViewerWidget::onGoToAddressInputChanged - ); - - QObject::connect( - &insightWorker, - &InsightWorker::targetStateUpdated, - this, - &HexViewerWidget::onTargetStateChanged - ); - - this->show(); -} - -void HexViewerWidget::updateValues(const Targets::TargetMemoryBuffer& buffer) { - this->byteItemGraphicsScene->updateValues(buffer); -} - -void HexViewerWidget::refreshRegions() { - this->byteItemGraphicsScene->refreshRegions(); -} - -void HexViewerWidget::setStackPointer(std::uint32_t stackPointer) { - this->byteItemGraphicsScene->updateStackPointer(stackPointer); -} - -void HexViewerWidget::resizeEvent(QResizeEvent* event) { - this->container->setFixedSize( - this->width(), - this->height() - ); - - this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size()); -} - -void HexViewerWidget::showEvent(QShowEvent* event) { - this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size()); -} - -void HexViewerWidget::onTargetStateChanged(Targets::TargetState newState) { - using Targets::TargetState; - this->targetState = newState; -} - -void HexViewerWidget::setStackMemoryHighlightingEnabled(bool enabled) { - this->highlightStackMemoryButton->setChecked(enabled); - this->settings.highlightStackMemory = enabled; - - this->byteItemGraphicsScene->invalidateChildItemCaches(); -} - -void HexViewerWidget::setHoveredRowAndColumnHighlightingEnabled(bool enabled) { - this->highlightHoveredRowAndColumnButton->setChecked(enabled); - this->settings.highlightHoveredRowAndCol = enabled; - - this->byteItemGraphicsScene->invalidateChildItemCaches(); -} - -void HexViewerWidget::setFocusedMemoryHighlightingEnabled(bool enabled) { - this->highlightFocusedMemoryButton->setChecked(enabled); - this->settings.highlightFocusedMemory = enabled; - - this->byteItemGraphicsScene->invalidateChildItemCaches(); -} - -void HexViewerWidget::setAnnotationsEnabled(bool enabled) { - this->displayAnnotationsButton->setChecked(enabled); - this->settings.displayAnnotations = enabled; - - this->byteItemGraphicsScene->adjustSize(true); -} - -void HexViewerWidget::setDisplayAsciiEnabled(bool enabled) { - this->displayAsciiButton->setChecked(enabled); - this->settings.displayAsciiValues = enabled; - - this->byteItemGraphicsScene->invalidateChildItemCaches(); -} - -void HexViewerWidget::onGoToAddressInputChanged() { - auto addressConversionOk = false; - const auto address = this->goToAddressInput->text().toUInt(&addressConversionOk, 16); - - const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange; - - if (addressConversionOk && memoryAddressRange.contains(address) && this->goToAddressInput->hasFocus()) { - this->byteItemGraphicsScene->setHighlightedAddresses({address}); - this->byteItemGraphicsView->scrollToByteItemAtAddress(address); - return; + void HexViewerWidget::updateValues(const Targets::TargetMemoryBuffer& buffer) { + this->byteItemGraphicsScene->updateValues(buffer); } - this->byteItemGraphicsScene->setHighlightedAddresses({}); + void HexViewerWidget::refreshRegions() { + this->byteItemGraphicsScene->refreshRegions(); + } + + void HexViewerWidget::setStackPointer(std::uint32_t stackPointer) { + this->byteItemGraphicsScene->updateStackPointer(stackPointer); + } + + void HexViewerWidget::resizeEvent(QResizeEvent* event) { + this->container->setFixedSize( + this->width(), + this->height() + ); + + this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size()); + } + + void HexViewerWidget::showEvent(QShowEvent* event) { + this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size()); + } + + void HexViewerWidget::onTargetStateChanged(Targets::TargetState newState) { + using Targets::TargetState; + this->targetState = newState; + } + + void HexViewerWidget::setStackMemoryHighlightingEnabled(bool enabled) { + this->highlightStackMemoryButton->setChecked(enabled); + this->settings.highlightStackMemory = enabled; + + this->byteItemGraphicsScene->invalidateChildItemCaches(); + } + + void HexViewerWidget::setHoveredRowAndColumnHighlightingEnabled(bool enabled) { + this->highlightHoveredRowAndColumnButton->setChecked(enabled); + this->settings.highlightHoveredRowAndCol = enabled; + + this->byteItemGraphicsScene->invalidateChildItemCaches(); + } + + void HexViewerWidget::setFocusedMemoryHighlightingEnabled(bool enabled) { + this->highlightFocusedMemoryButton->setChecked(enabled); + this->settings.highlightFocusedMemory = enabled; + + this->byteItemGraphicsScene->invalidateChildItemCaches(); + } + + void HexViewerWidget::setAnnotationsEnabled(bool enabled) { + this->displayAnnotationsButton->setChecked(enabled); + this->settings.displayAnnotations = enabled; + + this->byteItemGraphicsScene->adjustSize(true); + } + + void HexViewerWidget::setDisplayAsciiEnabled(bool enabled) { + this->displayAsciiButton->setChecked(enabled); + this->settings.displayAsciiValues = enabled; + + this->byteItemGraphicsScene->invalidateChildItemCaches(); + } + + void HexViewerWidget::onGoToAddressInputChanged() { + auto addressConversionOk = false; + const auto address = this->goToAddressInput->text().toUInt(&addressConversionOk, 16); + + const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange; + + if (addressConversionOk && memoryAddressRange.contains(address) && this->goToAddressInput->hasFocus()) { + this->byteItemGraphicsScene->setHighlightedAddresses({address}); + this->byteItemGraphicsView->scrollToByteItemAtAddress(address); + return; + } + + this->byteItemGraphicsScene->setHighlightedAddresses({}); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp index ebade458..a0c1fb65 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp @@ -2,68 +2,46 @@ #include -using namespace Bloom::Widgets; - -ValueAnnotationItem::ValueAnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion) - : AnnotationItem(focusedMemoryRegion, AnnotationItemPosition::TOP) - , focusedMemoryRegion(focusedMemoryRegion) - , endianness(focusedMemoryRegion.endianness) +namespace Bloom::Widgets { - this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT); -} - -void ValueAnnotationItem::setValue(const Targets::TargetMemoryBuffer& value) { - this->value = value; - - if (this->endianness == Targets::TargetMemoryEndianness::LITTLE) { - std::reverse(this->value.begin(), this->value.end()); - } - - this->refreshLabelText(); - this->setToolTip(this->labelText); -} - -void ValueAnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { - auto font = painter->font(); - font.setItalic(true); - painter->setFont(font); - - AnnotationItem::paint(painter, option, widget); -} - -void ValueAnnotationItem::refreshLabelText() { - this->update(); - - if (this->value.empty()) { + ValueAnnotationItem::ValueAnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion) + : AnnotationItem(focusedMemoryRegion, AnnotationItemPosition::TOP) + , focusedMemoryRegion(focusedMemoryRegion) + , endianness(focusedMemoryRegion.endianness) + { this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT); - return; } - switch (this->focusedMemoryRegion.dataType) { - case MemoryRegionDataType::UNSIGNED_INTEGER: { - std::uint64_t integerValue = 0; - for (const auto& byte : this->value) { - integerValue = (integerValue << 8) | byte; - } + void ValueAnnotationItem::setValue(const Targets::TargetMemoryBuffer& value) { + this->value = value; - this->labelText = QString::number(integerValue); - break; + if (this->endianness == Targets::TargetMemoryEndianness::LITTLE) { + std::reverse(this->value.begin(), this->value.end()); } - case MemoryRegionDataType::SIGNED_INTEGER: { - const auto valueSize = this->value.size(); - if (valueSize == 1) { - this->labelText = QString::number(static_cast(this->value[0])); - break; - } + this->refreshLabelText(); + this->setToolTip(this->labelText); + } - if (valueSize == 2) { - this->labelText = QString::number(static_cast((this->value[0] << 8) | this->value[1])); - break; - } + void ValueAnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { + auto font = painter->font(); + font.setItalic(true); + painter->setFont(font); - if (valueSize <= 4) { - std::int32_t integerValue = 0; + AnnotationItem::paint(painter, option, widget); + } + + void ValueAnnotationItem::refreshLabelText() { + this->update(); + + if (this->value.empty()) { + this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT); + return; + } + + switch (this->focusedMemoryRegion.dataType) { + case MemoryRegionDataType::UNSIGNED_INTEGER: { + std::uint64_t integerValue = 0; for (const auto& byte : this->value) { integerValue = (integerValue << 8) | byte; } @@ -71,42 +49,65 @@ void ValueAnnotationItem::refreshLabelText() { this->labelText = QString::number(integerValue); break; } + case MemoryRegionDataType::SIGNED_INTEGER: { + const auto valueSize = this->value.size(); - std::int64_t integerValue = 0; - for (const auto& byte : this->value) { - integerValue = (integerValue << 8) | byte; + if (valueSize == 1) { + this->labelText = QString::number(static_cast(this->value[0])); + break; + } + + if (valueSize == 2) { + this->labelText = QString::number(static_cast((this->value[0] << 8) | this->value[1])); + break; + } + + if (valueSize <= 4) { + std::int32_t integerValue = 0; + for (const auto& byte : this->value) { + integerValue = (integerValue << 8) | byte; + } + + this->labelText = QString::number(integerValue); + break; + } + + std::int64_t integerValue = 0; + for (const auto& byte : this->value) { + integerValue = (integerValue << 8) | byte; + } + + this->labelText = QString::number(integerValue); + break; } + case MemoryRegionDataType::ASCII_STRING: { + // Replace non-ASCII chars with '?' + auto asciiData = this->value; - this->labelText = QString::number(integerValue); - break; - } - case MemoryRegionDataType::ASCII_STRING: { - // Replace non-ASCII chars with '?' - auto asciiData = this->value; + std::replace_if( + asciiData.begin(), + asciiData.end(), + [] (unsigned char value) { + /* + * We only care about non-control characters (with the exception of the white space character) in + * the standard ASCII range. + */ + constexpr auto asciiRangeStart = 32; + constexpr auto asciiRangeEnd = 126; + return value < asciiRangeStart || value > asciiRangeEnd; + }, + '?' + ); - std::replace_if( - asciiData.begin(), - asciiData.end(), - [] (unsigned char value) { - /* - * We only care about non-control characters (with the exception of the white space character) in - * the standard ASCII range. - */ - constexpr auto asciiRangeStart = 32; - constexpr auto asciiRangeEnd = 126; - return value < asciiRangeStart || value > asciiRangeEnd; - }, - '?' - ); - - this->labelText = "'" + QString::fromLatin1( - reinterpret_cast(asciiData.data()), - static_cast(asciiData.size()) - ) + "'"; - break; - } - default: { - this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT); + this->labelText = "'" + QString::fromLatin1( + reinterpret_cast(asciiData.data()), + static_cast(asciiData.size()) + ) + "'"; + break; + } + default: { + this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT); + } } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.cpp index 3d602edd..c6c0cb77 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.cpp @@ -6,41 +6,42 @@ #include "src/Exceptions/Exception.hpp" #include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" -using namespace Bloom; -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + ExcludedRegionItem::ExcludedRegionItem( + const ExcludedMemoryRegion& region, + const Targets::TargetMemoryDescriptor& memoryDescriptor, + QWidget* parent + ): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) { + auto formUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" + + "/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui" + ) + ); -using Targets::TargetMemoryAddressRange; + if (!formUiFile.open(QFile::ReadOnly)) { + throw Bloom::Exceptions::Exception("Failed to open excluded region item form UI file"); + } -ExcludedRegionItem::ExcludedRegionItem( - const ExcludedMemoryRegion& region, - const Targets::TargetMemoryDescriptor& memoryDescriptor, - QWidget* parent -): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) { - auto formUiFile = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" - + "/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui" - ) - ); + auto uiLoader = UiLoader(this); + this->formWidget = uiLoader.load(&formUiFile, this); - if (!formUiFile.open(QFile::ReadOnly)) { - throw Bloom::Exceptions::Exception("Failed to open excluded region item form UI file"); + this->initFormInputs(); } - auto uiLoader = UiLoader(this); - this->formWidget = uiLoader.load(&formUiFile, this); + void ExcludedRegionItem::applyChanges() { + using Targets::TargetMemoryAddressRange; - this->initFormInputs(); -} - -void ExcludedRegionItem::applyChanges() { - this->memoryRegion.name = this->nameInput->text(); - - const auto inputAddressRange = TargetMemoryAddressRange( - this->startAddressInput->text().toUInt(nullptr, 16), - this->endAddressInput->text().toUInt(nullptr, 16) - ); - this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType(); - this->memoryRegion.addressRange = this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ? - this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange; + this->memoryRegion.name = this->nameInput->text(); + + const auto inputAddressRange = TargetMemoryAddressRange( + this->startAddressInput->text().toUInt(nullptr, 16), + this->endAddressInput->text().toUInt(nullptr, 16) + ); + this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType(); + this->memoryRegion.addressRange = + this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ? + this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange; + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.cpp index b5648c95..f9f14dd4 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.cpp @@ -6,99 +6,105 @@ #include "src/Exceptions/Exception.hpp" #include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" -using namespace Bloom; -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + using Targets::TargetMemoryAddressRange; -using Targets::TargetMemoryAddressRange; + FocusedRegionItem::FocusedRegionItem( + const FocusedMemoryRegion& region, + const Targets::TargetMemoryDescriptor& memoryDescriptor, + QWidget* parent + ): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) { + auto formUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" + + "/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui" + ) + ); -FocusedRegionItem::FocusedRegionItem( - const FocusedMemoryRegion& region, - const Targets::TargetMemoryDescriptor& memoryDescriptor, - QWidget* parent -): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) { - auto formUiFile = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" - + "/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui" - ) - ); - - if (!formUiFile.open(QFile::ReadOnly)) { - throw Bloom::Exceptions::Exception("Failed to open focused region item form UI file"); - } - - auto uiLoader = UiLoader(this); - this->formWidget = uiLoader.load(&formUiFile, this); - - this->initFormInputs(); -} - -void FocusedRegionItem::applyChanges() { - this->memoryRegion.name = this->nameInput->text(); - - const auto inputAddressRange = TargetMemoryAddressRange( - this->startAddressInput->text().toUInt(nullptr, 16), - this->endAddressInput->text().toUInt(nullptr, 16) - ); - this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType(); - this->memoryRegion.addressRange = - this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ? - this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange; - - auto selectedDataTypeOptionName = this->dataTypeInput->currentData().toString(); - if (FocusedRegionItem::dataTypeOptionsByName.contains(selectedDataTypeOptionName)) { - this->memoryRegion.dataType = FocusedRegionItem::dataTypeOptionsByName.at(selectedDataTypeOptionName).dataType; - } - - auto selectedEndiannessOptionName = this->endiannessInput->currentData().toString(); - if (FocusedRegionItem::endiannessOptionsByName.contains(selectedEndiannessOptionName)) { - this->memoryRegion.endianness = FocusedRegionItem::endiannessOptionsByName.at( - selectedEndiannessOptionName - ).endianness; - } -} - -void FocusedRegionItem::initFormInputs() { - RegionItem::initFormInputs(); - const auto& region = this->memoryRegion; - - this->dataTypeInput = this->formWidget->findChild("data-type-input"); - this->endiannessInput = this->formWidget->findChild("endianness-input"); - - for (const auto& [optionName, option] : FocusedRegionItem::dataTypeOptionsByName) { - this->dataTypeInput->addItem(option.text, optionName); - } - - for (const auto& [optionName, option] : FocusedRegionItem::endiannessOptionsByName) { - this->endiannessInput->addItem(option.text, optionName); - } - - switch (region.dataType) { - case MemoryRegionDataType::UNSIGNED_INTEGER: { - this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("unsigned_integer").text); - break; + if (!formUiFile.open(QFile::ReadOnly)) { + throw Bloom::Exceptions::Exception("Failed to open focused region item form UI file"); } - case MemoryRegionDataType::SIGNED_INTEGER: { - this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("signed_integer").text); - break; + + auto uiLoader = UiLoader(this); + this->formWidget = uiLoader.load(&formUiFile, this); + + this->initFormInputs(); + } + + void FocusedRegionItem::applyChanges() { + this->memoryRegion.name = this->nameInput->text(); + + const auto inputAddressRange = TargetMemoryAddressRange( + this->startAddressInput->text().toUInt(nullptr, 16), + this->endAddressInput->text().toUInt(nullptr, 16) + ); + this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType(); + this->memoryRegion.addressRange = + this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ? + this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange; + + auto selectedDataTypeOptionName = this->dataTypeInput->currentData().toString(); + if (FocusedRegionItem::dataTypeOptionsByName.contains(selectedDataTypeOptionName)) { + this->memoryRegion.dataType = FocusedRegionItem::dataTypeOptionsByName.at( + selectedDataTypeOptionName + ).dataType; } - case MemoryRegionDataType::ASCII_STRING: { - this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("ascii").text); - break; - } - default: { - this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("other").text); + + auto selectedEndiannessOptionName = this->endiannessInput->currentData().toString(); + if (FocusedRegionItem::endiannessOptionsByName.contains(selectedEndiannessOptionName)) { + this->memoryRegion.endianness = FocusedRegionItem::endiannessOptionsByName.at( + selectedEndiannessOptionName + ).endianness; } } - switch (region.endianness) { - case Targets::TargetMemoryEndianness::LITTLE: { - this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("little").text); - break; + void FocusedRegionItem::initFormInputs() { + RegionItem::initFormInputs(); + const auto& region = this->memoryRegion; + + this->dataTypeInput = this->formWidget->findChild("data-type-input"); + this->endiannessInput = this->formWidget->findChild("endianness-input"); + + for (const auto& [optionName, option] : FocusedRegionItem::dataTypeOptionsByName) { + this->dataTypeInput->addItem(option.text, optionName); } - case Targets::TargetMemoryEndianness::BIG: { - this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("big").text); - break; + + for (const auto& [optionName, option] : FocusedRegionItem::endiannessOptionsByName) { + this->endiannessInput->addItem(option.text, optionName); + } + + switch (region.dataType) { + case MemoryRegionDataType::UNSIGNED_INTEGER: { + this->dataTypeInput->setCurrentText( + FocusedRegionItem::dataTypeOptionsByName.at("unsigned_integer").text + ); + break; + } + case MemoryRegionDataType::SIGNED_INTEGER: { + this->dataTypeInput->setCurrentText( + FocusedRegionItem::dataTypeOptionsByName.at("signed_integer").text + ); + break; + } + case MemoryRegionDataType::ASCII_STRING: { + this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("ascii").text); + break; + } + default: { + this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("other").text); + } + } + + switch (region.endianness) { + case Targets::TargetMemoryEndianness::LITTLE: { + this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("little").text); + break; + } + case Targets::TargetMemoryEndianness::BIG: { + this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("big").text); + break; + } } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp index 4e4e45df..f0fecda2 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp @@ -10,383 +10,401 @@ #include "src/Helpers/Paths.hpp" #include "src/Exceptions/Exception.hpp" -using namespace Bloom; -using namespace Bloom::Widgets; - -using Bloom::Exceptions::Exception; - -MemoryRegionManagerWindow::MemoryRegionManagerWindow( - const Targets::TargetMemoryDescriptor& memoryDescriptor, - std::vector& focusedMemoryRegions, - std::vector& excludedMemoryRegions, - QWidget* parent -): - QWidget(parent), - memoryDescriptor(memoryDescriptor), - focusedMemoryRegions(focusedMemoryRegions), - excludedMemoryRegions(excludedMemoryRegions) +namespace Bloom::Widgets { - this->setWindowFlag(Qt::Window); - this->setObjectName("memory-region-manager-window"); - this->setWindowTitle( - "Memory Regions - " - + QString(this->memoryDescriptor.type == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM") - ); + using Bloom::Exceptions::Exception; - auto windowUiFile = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" - + "/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui" - ) - ); - - auto windowStylesheet = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" - + "/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss" - ) - ); - - if (!windowUiFile.open(QFile::ReadOnly)) { - throw Exception("Failed to open MemoryRegionManagerWindow UI file"); - } - - if (!windowStylesheet.open(QFile::ReadOnly)) { - throw Exception("Failed to open MemoryRegionManagerWindow stylesheet file"); - } - - this->setStyleSheet(windowStylesheet.readAll()); - this->setFixedSize(QSize(970, 540)); - - auto uiLoader = UiLoader(this); - this->container = uiLoader.load(&windowUiFile, this); - - this->container->setFixedSize(this->size()); - this->container->setContentsMargins(0, 0, 0, 0); - - this->regionSelector = this->container->findChild("region-selector"); - auto* regionSelectorToolBar = this->regionSelector->findChild("region-selector-tool-bar"); - this->addRegionButton = this->regionSelector->findChild("add-region-btn"); - this->removeRegionButton = this->regionSelector->findChild("remove-region-btn"); - this->addFocusedRegionMenuAction = this->addRegionButton->findChild("add-focused-region"); - this->addExcludedRegionMenuAction = this->addRegionButton->findChild("add-excluded-region"); - this->regionItemScrollArea = this->regionSelector->findChild("region-item-scroll-area"); - this->regionItemScrollAreaViewport = this->regionItemScrollArea->findChild("item-container"); - this->regionItemScrollAreaViewportLayout = this->regionItemScrollAreaViewport->findChild( - "item-container-layout" - ); - - this->stackedFormLayout = this->container->findChild("stacked-form-layout"); - - this->applyButton = this->container->findChild("apply-btn"); - this->helpButton = this->container->findChild("help-btn"); - this->closeButton = this->container->findChild("close-btn"); - - regionSelectorToolBar->setContentsMargins(0, 0, 0, 0); - this->regionItemScrollArea->setContentsMargins(0, 0, 0, 0); - this->regionItemScrollAreaViewport->setContentsMargins(0, 0, 0, 0); - this->regionItemScrollAreaViewportLayout->setContentsMargins(0, 0, 0, 0); - this->regionItemScrollAreaViewportLayout->setDirection(QBoxLayout::Direction::TopToBottom); - - QObject::connect( - this->addFocusedRegionMenuAction, - &QAction::triggered, - this, - &MemoryRegionManagerWindow::onNewFocusedRegionTrigger - ); - - QObject::connect( - this->addExcludedRegionMenuAction, - &QAction::triggered, - this, - &MemoryRegionManagerWindow::onNewExcludedRegionTrigger - ); - - QObject::connect( - this->removeRegionButton, - &QToolButton::clicked, - this, - &MemoryRegionManagerWindow::onDeleteRegionTrigger - ); - - QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close); - QObject::connect(this->applyButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::applyChanges); - QObject::connect(this->helpButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::openHelpPage); - - this->refreshRegions(); - - // Position the inspection window at the center of the main Insight window - this->move(parent->window()->geometry().center() - this->rect().center()); - this->show(); -} - -void MemoryRegionManagerWindow::refreshRegions() { - this->clearRegions(); - - for (const auto& focusedRegion: this->focusedMemoryRegions) { - this->addFocusedRegion(focusedRegion); - } - - for (const auto& excludedRegion: this->excludedMemoryRegions) { - this->addExcludedRegion(excludedRegion); - } - - this->sortRegionItems(); -} - -void MemoryRegionManagerWindow::showEvent(QShowEvent* event) { - if (this->selectedRegion == nullptr && this->regionItemScrollAreaViewportLayout->count() > 0) { - auto* firstRegionItem = qobject_cast( - this->regionItemScrollAreaViewportLayout->itemAt(0)->widget() + MemoryRegionManagerWindow::MemoryRegionManagerWindow( + const Targets::TargetMemoryDescriptor& memoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, + QWidget* parent + ) + : QWidget(parent) + , memoryDescriptor(memoryDescriptor) + , focusedMemoryRegions(focusedMemoryRegions) + , excludedMemoryRegions(excludedMemoryRegions) + { + this->setWindowFlag(Qt::Window); + this->setObjectName("memory-region-manager-window"); + this->setWindowTitle( + "Memory Regions - " + + QString(this->memoryDescriptor.type == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM") ); - if (firstRegionItem != nullptr) { - firstRegionItem->setSelected(true); - } - } -} - -void MemoryRegionManagerWindow::clearRegions() { - this->selectedRegion = nullptr; - - for (auto* focusedRegionItem: this->focusedRegionItems) { - this->regionItemScrollAreaViewportLayout->removeWidget(focusedRegionItem); - - focusedRegionItem->getFormWidget()->deleteLater(); - focusedRegionItem->deleteLater(); - } - - for (auto* excludedRegionItem: this->excludedRegionItems) { - this->regionItemScrollAreaViewportLayout->removeWidget(excludedRegionItem); - - excludedRegionItem->getFormWidget()->deleteLater(); - excludedRegionItem->deleteLater(); - } - - this->focusedRegionItems.clear(); - this->excludedRegionItems.clear(); -} - -void MemoryRegionManagerWindow::sortRegionItems() { - /* - * This isn't very pretty. - * - * Because the insertion order is persisted in QBoxLayouts, sorting the items requires removing them from the - * layout, and then re-inserting them in the correct order. - */ - auto regionItemCompare = [] (RegionItem* itemA, RegionItem* itemB) { - return itemA->getMemoryRegion().createdDate < itemB->getMemoryRegion().createdDate; - }; - auto sortedRegionItems = std::set(regionItemCompare); - - QLayoutItem* layoutItem = nullptr; - while ((layoutItem = this->regionItemScrollAreaViewportLayout->takeAt(0)) != nullptr) { - auto* regionItem = qobject_cast(layoutItem->widget()); - if (regionItem != nullptr) { - sortedRegionItems.insert(regionItem); - } - - delete layoutItem; - } - - for (auto* regionItem: sortedRegionItems) { - this->regionItemScrollAreaViewportLayout->addWidget(regionItem); - } -} - -FocusedRegionItem* MemoryRegionManagerWindow::addFocusedRegion(const FocusedMemoryRegion& region) { - auto* focusedRegionItem = new FocusedRegionItem(region, this->memoryDescriptor, this->regionItemScrollAreaViewport); - this->focusedRegionItems.insert(focusedRegionItem); - - this->regionItemScrollAreaViewportLayout->addWidget(focusedRegionItem); - this->stackedFormLayout->addWidget(focusedRegionItem->getFormWidget()); - - QObject::connect(focusedRegionItem, &RegionItem::selected, this, &MemoryRegionManagerWindow::onRegionSelected); - - return focusedRegionItem; -} - -ExcludedRegionItem* MemoryRegionManagerWindow::addExcludedRegion(const ExcludedMemoryRegion& region) { - auto* excludedRegionItem = new ExcludedRegionItem(region, this->memoryDescriptor, this->regionItemScrollAreaViewport); - this->excludedRegionItems.insert(excludedRegionItem); - - this->regionItemScrollAreaViewportLayout->addWidget(excludedRegionItem); - this->stackedFormLayout->addWidget(excludedRegionItem->getFormWidget()); - - QObject::connect(excludedRegionItem, &RegionItem::selected, this, &MemoryRegionManagerWindow::onRegionSelected); - - return excludedRegionItem; -} - -void MemoryRegionManagerWindow::onRegionSelected(RegionItem* selectedRegion) { - if (this->selectedRegion != nullptr && this->selectedRegion != selectedRegion) { - this->selectedRegion->setSelected(false); - } - - this->selectedRegion = selectedRegion; - this->stackedFormLayout->setCurrentWidget(this->selectedRegion->getFormWidget()); -} - -void MemoryRegionManagerWindow::onNewFocusedRegionTrigger() { - using Targets::TargetMemoryAddressRange; - - auto* region = this->addFocusedRegion(FocusedMemoryRegion( - "Untitled Region", - TargetMemoryAddressRange( - this->memoryDescriptor.addressRange.startAddress, - this->memoryDescriptor.addressRange.startAddress + 10 - ) - )); - - region->setSelected(true); -} - -void MemoryRegionManagerWindow::onNewExcludedRegionTrigger() { - using Targets::TargetMemoryAddressRange; - - auto* region = this->addExcludedRegion(ExcludedMemoryRegion( - "Untitled Region", - TargetMemoryAddressRange( - this->memoryDescriptor.addressRange.startAddress, - this->memoryDescriptor.addressRange.startAddress + 10 - ) - )); - - region->setSelected(true); -} - -void MemoryRegionManagerWindow::onDeleteRegionTrigger() { - if (this->selectedRegion == nullptr) { - return; - } - - auto* regionItem = this->selectedRegion; - const auto& region = regionItem->getMemoryRegion(); - - if (region.type == MemoryRegionType::FOCUSED) { - auto* focusedRegionItem = qobject_cast(regionItem); - - if (focusedRegionItem != nullptr) { - this->focusedRegionItems.erase(focusedRegionItem); - } - - } else { - auto* excludedRegionItem = qobject_cast(regionItem); - - if (excludedRegionItem != nullptr) { - this->excludedRegionItems.erase(excludedRegionItem); - } - } - - regionItem->getFormWidget()->deleteLater(); - this->regionItemScrollAreaViewportLayout->removeWidget(regionItem); - regionItem->deleteLater(); - - this->selectedRegion = nullptr; - - if (this->regionItemScrollAreaViewportLayout->count() > 0) { - auto* regionItem = qobject_cast( - this->regionItemScrollAreaViewportLayout->itemAt(0)->widget() + auto windowUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" + + "/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui" + ) ); - if (regionItem != nullptr) { - regionItem->setSelected(true); + auto windowStylesheet = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" + + "/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss" + ) + ); + + if (!windowUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open MemoryRegionManagerWindow UI file"); } + + if (!windowStylesheet.open(QFile::ReadOnly)) { + throw Exception("Failed to open MemoryRegionManagerWindow stylesheet file"); + } + + this->setStyleSheet(windowStylesheet.readAll()); + this->setFixedSize(QSize(970, 540)); + + auto uiLoader = UiLoader(this); + this->container = uiLoader.load(&windowUiFile, this); + + this->container->setFixedSize(this->size()); + this->container->setContentsMargins(0, 0, 0, 0); + + this->regionSelector = this->container->findChild("region-selector"); + auto* regionSelectorToolBar = this->regionSelector->findChild("region-selector-tool-bar"); + this->addRegionButton = this->regionSelector->findChild("add-region-btn"); + this->removeRegionButton = this->regionSelector->findChild("remove-region-btn"); + this->addFocusedRegionMenuAction = this->addRegionButton->findChild("add-focused-region"); + this->addExcludedRegionMenuAction = this->addRegionButton->findChild("add-excluded-region"); + this->regionItemScrollArea = this->regionSelector->findChild("region-item-scroll-area"); + this->regionItemScrollAreaViewport = this->regionItemScrollArea->findChild("item-container"); + this->regionItemScrollAreaViewportLayout = this->regionItemScrollAreaViewport->findChild( + "item-container-layout" + ); + + this->stackedFormLayout = this->container->findChild("stacked-form-layout"); + + this->applyButton = this->container->findChild("apply-btn"); + this->helpButton = this->container->findChild("help-btn"); + this->closeButton = this->container->findChild("close-btn"); + + regionSelectorToolBar->setContentsMargins(0, 0, 0, 0); + this->regionItemScrollArea->setContentsMargins(0, 0, 0, 0); + this->regionItemScrollAreaViewport->setContentsMargins(0, 0, 0, 0); + this->regionItemScrollAreaViewportLayout->setContentsMargins(0, 0, 0, 0); + this->regionItemScrollAreaViewportLayout->setDirection(QBoxLayout::Direction::TopToBottom); + + QObject::connect( + this->addFocusedRegionMenuAction, + &QAction::triggered, + this, + &MemoryRegionManagerWindow::onNewFocusedRegionTrigger + ); + + QObject::connect( + this->addExcludedRegionMenuAction, + &QAction::triggered, + this, + &MemoryRegionManagerWindow::onNewExcludedRegionTrigger + ); + + QObject::connect( + this->removeRegionButton, + &QToolButton::clicked, + this, + &MemoryRegionManagerWindow::onDeleteRegionTrigger + ); + + QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close); + QObject::connect(this->applyButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::applyChanges); + QObject::connect(this->helpButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::openHelpPage); + + this->refreshRegions(); + + // Position the inspection window at the center of the main Insight window + this->move(parent->window()->geometry().center() - this->rect().center()); + this->show(); } -} -void MemoryRegionManagerWindow::applyChanges() { - auto processedFocusedMemoryRegions = std::vector(); - auto processedExcludedMemoryRegions = std::vector(); + void MemoryRegionManagerWindow::refreshRegions() { + this->clearRegions(); - for (auto* focusedRegionItem : this->focusedRegionItems) { - const auto validationFailures = focusedRegionItem->getValidationFailures(); + for (const auto& focusedRegion: this->focusedMemoryRegions) { + this->addFocusedRegion(focusedRegion); + } - if (!validationFailures.empty()) { - auto* errorDialogue = new ErrorDialogue( - "Invalid Memory Region", - "Invalid memory region \"" + focusedRegionItem->getRegionNameInputValue() + "\"" - + "

- " + validationFailures.join("
- "), - this + for (const auto& excludedRegion: this->excludedMemoryRegions) { + this->addExcludedRegion(excludedRegion); + } + + this->sortRegionItems(); + } + + void MemoryRegionManagerWindow::showEvent(QShowEvent* event) { + if (this->selectedRegion == nullptr && this->regionItemScrollAreaViewportLayout->count() > 0) { + auto* firstRegionItem = qobject_cast( + this->regionItemScrollAreaViewportLayout->itemAt(0)->widget() ); - errorDialogue->show(); + + if (firstRegionItem != nullptr) { + firstRegionItem->setSelected(true); + } + } + } + + void MemoryRegionManagerWindow::clearRegions() { + this->selectedRegion = nullptr; + + for (auto* focusedRegionItem: this->focusedRegionItems) { + this->regionItemScrollAreaViewportLayout->removeWidget(focusedRegionItem); + + focusedRegionItem->getFormWidget()->deleteLater(); + focusedRegionItem->deleteLater(); + } + + for (auto* excludedRegionItem: this->excludedRegionItems) { + this->regionItemScrollAreaViewportLayout->removeWidget(excludedRegionItem); + + excludedRegionItem->getFormWidget()->deleteLater(); + excludedRegionItem->deleteLater(); + } + + this->focusedRegionItems.clear(); + this->excludedRegionItems.clear(); + } + + void MemoryRegionManagerWindow::sortRegionItems() { + /* + * This isn't very pretty. + * + * Because the insertion order is persisted in QBoxLayouts, sorting the items requires removing them from the + * layout, and then re-inserting them in the correct order. + */ + auto regionItemCompare = [] (RegionItem* itemA, RegionItem* itemB) { + return itemA->getMemoryRegion().createdDate < itemB->getMemoryRegion().createdDate; + }; + auto sortedRegionItems = std::set(regionItemCompare); + + QLayoutItem* layoutItem = nullptr; + while ((layoutItem = this->regionItemScrollAreaViewportLayout->takeAt(0)) != nullptr) { + auto* regionItem = qobject_cast(layoutItem->widget()); + if (regionItem != nullptr) { + sortedRegionItems.insert(regionItem); + } + + delete layoutItem; + } + + for (auto* regionItem: sortedRegionItems) { + this->regionItemScrollAreaViewportLayout->addWidget(regionItem); + } + } + + FocusedRegionItem* MemoryRegionManagerWindow::addFocusedRegion(const FocusedMemoryRegion& region) { + auto* focusedRegionItem = new FocusedRegionItem( + region, + this->memoryDescriptor, + this->regionItemScrollAreaViewport + ); + this->focusedRegionItems.insert(focusedRegionItem); + + this->regionItemScrollAreaViewportLayout->addWidget(focusedRegionItem); + this->stackedFormLayout->addWidget(focusedRegionItem->getFormWidget()); + + QObject::connect( + focusedRegionItem, + &RegionItem::selected, + this, + &MemoryRegionManagerWindow::onRegionSelected + ); + + return focusedRegionItem; + } + + ExcludedRegionItem* MemoryRegionManagerWindow::addExcludedRegion(const ExcludedMemoryRegion& region) { + auto* excludedRegionItem = new ExcludedRegionItem( + region, + this->memoryDescriptor, + this->regionItemScrollAreaViewport + ); + this->excludedRegionItems.insert(excludedRegionItem); + + this->regionItemScrollAreaViewportLayout->addWidget(excludedRegionItem); + this->stackedFormLayout->addWidget(excludedRegionItem->getFormWidget()); + + QObject::connect( + excludedRegionItem, + &RegionItem::selected, + this, + &MemoryRegionManagerWindow::onRegionSelected + ); + + return excludedRegionItem; + } + + void MemoryRegionManagerWindow::onRegionSelected(RegionItem* selectedRegion) { + if (this->selectedRegion != nullptr && this->selectedRegion != selectedRegion) { + this->selectedRegion->setSelected(false); + } + + this->selectedRegion = selectedRegion; + this->stackedFormLayout->setCurrentWidget(this->selectedRegion->getFormWidget()); + } + + void MemoryRegionManagerWindow::onNewFocusedRegionTrigger() { + using Targets::TargetMemoryAddressRange; + + auto* region = this->addFocusedRegion(FocusedMemoryRegion( + "Untitled Region", + TargetMemoryAddressRange( + this->memoryDescriptor.addressRange.startAddress, + this->memoryDescriptor.addressRange.startAddress + 10 + ) + )); + + region->setSelected(true); + } + + void MemoryRegionManagerWindow::onNewExcludedRegionTrigger() { + using Targets::TargetMemoryAddressRange; + + auto* region = this->addExcludedRegion(ExcludedMemoryRegion( + "Untitled Region", + TargetMemoryAddressRange( + this->memoryDescriptor.addressRange.startAddress, + this->memoryDescriptor.addressRange.startAddress + 10 + ) + )); + + region->setSelected(true); + } + + void MemoryRegionManagerWindow::onDeleteRegionTrigger() { + if (this->selectedRegion == nullptr) { return; } - focusedRegionItem->applyChanges(); - const auto& focusedRegion = focusedRegionItem->getMemoryRegion(); - for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) { - if (processedFocusedRegion.intersectsWith(focusedRegion)) { - auto* errorDialogue = new ErrorDialogue( - "Intersecting Region Found", - "Region \"" + focusedRegionItem->getRegionNameInputValue() - + "\" intersects with region \"" + processedFocusedRegion.name + "\". " - + "Regions cannot intersect. Please review the relevant address ranges.", - this - ); - errorDialogue->show(); - return; + auto* regionItem = this->selectedRegion; + const auto& region = regionItem->getMemoryRegion(); + + if (region.type == MemoryRegionType::FOCUSED) { + auto* focusedRegionItem = qobject_cast(regionItem); + + if (focusedRegionItem != nullptr) { + this->focusedRegionItems.erase(focusedRegionItem); + } + + } else { + auto* excludedRegionItem = qobject_cast(regionItem); + + if (excludedRegionItem != nullptr) { + this->excludedRegionItems.erase(excludedRegionItem); } } - processedFocusedMemoryRegions.emplace_back(focusedRegion); - } + regionItem->getFormWidget()->deleteLater(); + this->regionItemScrollAreaViewportLayout->removeWidget(regionItem); + regionItem->deleteLater(); - for (auto* excludedRegionItem : this->excludedRegionItems) { - const auto validationFailures = excludedRegionItem->getValidationFailures(); + this->selectedRegion = nullptr; - if (!validationFailures.empty()) { - auto* errorDialogue = new ErrorDialogue( - "Invalid Memory Region", - "Invalid memory region \"" + excludedRegionItem->getRegionNameInputValue() + "\"" - + "

- " + validationFailures.join("
- "), - this + if (this->regionItemScrollAreaViewportLayout->count() > 0) { + auto* regionItem = qobject_cast( + this->regionItemScrollAreaViewportLayout->itemAt(0)->widget() ); - errorDialogue->show(); - return; - } - excludedRegionItem->applyChanges(); - auto excludedRegion = excludedRegionItem->getMemoryRegion(); - for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) { - if (processedFocusedRegion.intersectsWith(excludedRegion)) { - auto* errorDialogue = new ErrorDialogue( - "Intersecting Region Found", - "Region \"" + excludedRegionItem->getRegionNameInputValue() - + "\" intersects with region \"" + processedFocusedRegion.name + "\". " - + "Regions cannot intersect. Please review the relevant address ranges.", - this - ); - errorDialogue->show(); - return; + if (regionItem != nullptr) { + regionItem->setSelected(true); } } - - for (const auto& processedExcludedRegion : processedExcludedMemoryRegions) { - if (processedExcludedRegion.intersectsWith(excludedRegion)) { - auto* errorDialogue = new ErrorDialogue( - "Intersecting Region Found", - "Region \"" + excludedRegionItem->getRegionNameInputValue() - + "\" intersects with region \"" + processedExcludedRegion.name + "\". " - + "Regions cannot intersect. Please review the relevant address ranges.", - this - ); - errorDialogue->show(); - return; - } - } - - processedExcludedMemoryRegions.emplace_back(excludedRegion); } - this->focusedMemoryRegions = std::move(processedFocusedMemoryRegions); - this->excludedMemoryRegions = std::move(processedExcludedMemoryRegions); - this->close(); - emit this->changesApplied(); -} + void MemoryRegionManagerWindow::applyChanges() { + auto processedFocusedMemoryRegions = std::vector(); + auto processedExcludedMemoryRegions = std::vector(); -void MemoryRegionManagerWindow::openHelpPage() { - QDesktopServices::openUrl( - QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/manage-memory-regions")) - ); + for (auto* focusedRegionItem : this->focusedRegionItems) { + const auto validationFailures = focusedRegionItem->getValidationFailures(); + + if (!validationFailures.empty()) { + auto* errorDialogue = new ErrorDialogue( + "Invalid Memory Region", + "Invalid memory region \"" + focusedRegionItem->getRegionNameInputValue() + "\"" + + "

- " + validationFailures.join("
- "), + this + ); + errorDialogue->show(); + return; + } + + focusedRegionItem->applyChanges(); + const auto& focusedRegion = focusedRegionItem->getMemoryRegion(); + for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) { + if (processedFocusedRegion.intersectsWith(focusedRegion)) { + auto* errorDialogue = new ErrorDialogue( + "Intersecting Region Found", + "Region \"" + focusedRegionItem->getRegionNameInputValue() + + "\" intersects with region \"" + processedFocusedRegion.name + "\". " + + "Regions cannot intersect. Please review the relevant address ranges.", + this + ); + errorDialogue->show(); + return; + } + } + + processedFocusedMemoryRegions.emplace_back(focusedRegion); + } + + for (auto* excludedRegionItem : this->excludedRegionItems) { + const auto validationFailures = excludedRegionItem->getValidationFailures(); + + if (!validationFailures.empty()) { + auto* errorDialogue = new ErrorDialogue( + "Invalid Memory Region", + "Invalid memory region \"" + excludedRegionItem->getRegionNameInputValue() + "\"" + + "

- " + validationFailures.join("
- "), + this + ); + errorDialogue->show(); + return; + } + + excludedRegionItem->applyChanges(); + auto excludedRegion = excludedRegionItem->getMemoryRegion(); + for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) { + if (processedFocusedRegion.intersectsWith(excludedRegion)) { + auto* errorDialogue = new ErrorDialogue( + "Intersecting Region Found", + "Region \"" + excludedRegionItem->getRegionNameInputValue() + + "\" intersects with region \"" + processedFocusedRegion.name + "\". " + + "Regions cannot intersect. Please review the relevant address ranges.", + this + ); + errorDialogue->show(); + return; + } + } + + for (const auto& processedExcludedRegion : processedExcludedMemoryRegions) { + if (processedExcludedRegion.intersectsWith(excludedRegion)) { + auto* errorDialogue = new ErrorDialogue( + "Intersecting Region Found", + "Region \"" + excludedRegionItem->getRegionNameInputValue() + + "\" intersects with region \"" + processedExcludedRegion.name + "\". " + + "Regions cannot intersect. Please review the relevant address ranges.", + this + ); + errorDialogue->show(); + return; + } + } + + processedExcludedMemoryRegions.emplace_back(excludedRegion); + } + + this->focusedMemoryRegions = std::move(processedFocusedMemoryRegions); + this->excludedMemoryRegions = std::move(processedExcludedMemoryRegions); + this->close(); + emit this->changesApplied(); + } + + void MemoryRegionManagerWindow::openHelpPage() { + QDesktopServices::openUrl( + QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/manage-memory-regions")) + ); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.cpp index a16e9a97..58e0ee0c 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.cpp @@ -3,217 +3,228 @@ #include #include -using namespace Bloom; -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + using Targets::TargetMemoryAddressRange; -using Targets::TargetMemoryAddressRange; + RegionItem::RegionItem( + const MemoryRegion& region, + const Targets::TargetMemoryDescriptor& memoryDescriptor, + QWidget* parent + ): memoryDescriptor(memoryDescriptor), ClickableWidget(parent) { + this->setObjectName("region-item"); + this->setFixedHeight(50); + this->layout->setContentsMargins(5, 5, 5, 0); -RegionItem::RegionItem( - const MemoryRegion& region, - const Targets::TargetMemoryDescriptor& memoryDescriptor, - QWidget* parent -): memoryDescriptor(memoryDescriptor), ClickableWidget(parent) { - this->setObjectName("region-item"); - this->setFixedHeight(50); - this->layout->setContentsMargins(5, 5, 5, 0); + this->timeLabel->setText(region.createdDate.toString("hh:mm")); + this->timeLabel->setObjectName("time-label"); - this->timeLabel->setText(region.createdDate.toString("hh:mm")); - this->timeLabel->setObjectName("time-label"); + auto regionName = region.name; + regionName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH); + this->nameLabel->setText(regionName); + this->nameLabel->setObjectName("name-label"); - auto regionName = region.name; - regionName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH); - this->nameLabel->setText(regionName); - this->nameLabel->setObjectName("name-label"); + this->typeLabel->setText(region.type == MemoryRegionType::FOCUSED ? "Focused" : "Excluded"); + this->typeLabel->setObjectName("type-label"); - this->typeLabel->setText(region.type == MemoryRegionType::FOCUSED ? "Focused" : "Excluded"); - this->typeLabel->setObjectName("type-label"); + this->addressRangeLabel->setText( + "0x" + QString::number(region.addressRange.startAddress, 16).toUpper() + QString(" -> ") + + "0x" + QString::number(region.addressRange.endAddress, 16).toUpper() + ); + this->addressRangeLabel->setObjectName("address-label"); - this->addressRangeLabel->setText( - "0x" + QString::number(region.addressRange.startAddress, 16).toUpper() + QString(" -> ") - + "0x" + QString::number(region.addressRange.endAddress, 16).toUpper() - ); - this->addressRangeLabel->setObjectName("address-label"); + auto* topLabelLayout = new QHBoxLayout(); + topLabelLayout->setSpacing(0); + topLabelLayout->setContentsMargins(0, 0, 0, 0); + topLabelLayout->addWidget(this->nameLabel, 0, Qt::AlignmentFlag::AlignLeft); + topLabelLayout->addStretch(1); + topLabelLayout->addWidget(this->typeLabel, 0, Qt::AlignmentFlag::AlignRight); - auto* topLabelLayout = new QHBoxLayout(); - topLabelLayout->setSpacing(0); - topLabelLayout->setContentsMargins(0, 0, 0, 0); - topLabelLayout->addWidget(this->nameLabel, 0, Qt::AlignmentFlag::AlignLeft); - topLabelLayout->addStretch(1); - topLabelLayout->addWidget(this->typeLabel, 0, Qt::AlignmentFlag::AlignRight); + auto* bottomLabelLayout = new QHBoxLayout(); + bottomLabelLayout->setSpacing(0); + bottomLabelLayout->setContentsMargins(0, 0, 0, 0); + bottomLabelLayout->addWidget(this->addressRangeLabel, 0, Qt::AlignmentFlag::AlignLeft); + bottomLabelLayout->addStretch(1); + bottomLabelLayout->addWidget(this->timeLabel, 0, Qt::AlignmentFlag::AlignRight); - auto* bottomLabelLayout = new QHBoxLayout(); - bottomLabelLayout->setSpacing(0); - bottomLabelLayout->setContentsMargins(0, 0, 0, 0); - bottomLabelLayout->addWidget(this->addressRangeLabel, 0, Qt::AlignmentFlag::AlignLeft); - bottomLabelLayout->addStretch(1); - bottomLabelLayout->addWidget(this->timeLabel, 0, Qt::AlignmentFlag::AlignRight); + this->layout->setSpacing(5); + this->layout->addLayout(topLabelLayout); + this->layout->addLayout(bottomLabelLayout); + this->layout->addStretch(1); - this->layout->setSpacing(5); - this->layout->addLayout(topLabelLayout); - this->layout->addLayout(bottomLabelLayout); - this->layout->addStretch(1); + auto onClick = [this] { + this->setSelected(true); + }; - auto onClick = [this] { - this->setSelected(true); - }; + QObject::connect(this, &ClickableWidget::clicked, this, onClick); + QObject::connect(this, &ClickableWidget::rightClicked, this, onClick); - QObject::connect(this, &ClickableWidget::clicked, this, onClick); - QObject::connect(this, &ClickableWidget::rightClicked, this, onClick); - - this->setSelected(false); -} - -void RegionItem::setSelected(bool selected) { - this->setProperty("selected", selected); - this->style()->unpolish(this); - this->style()->polish(this); - - if (selected) { - emit this->selected(this); - } -} - -QStringList RegionItem::getValidationFailures() const { - auto validationFailures = QStringList(); - - if (this->nameInput->text().isEmpty()) { - validationFailures.emplace_back("Missing region name."); + this->setSelected(false); } - bool conversionOk = false; + void RegionItem::setSelected(bool selected) { + this->setProperty("selected", selected); + this->style()->unpolish(this); + this->style()->polish(this); - std::uint32_t startAddress = this->startAddressInput->text().toUInt(&conversionOk, 16); - if (!conversionOk) { - validationFailures.emplace_back("Invalid start address."); + if (selected) { + emit this->selected(this); + } } - std::uint32_t endAddress = this->endAddressInput->text().toUInt(&conversionOk, 16); - if (!conversionOk) { - validationFailures.emplace_back("Invalid end address."); + QStringList RegionItem::getValidationFailures() const { + auto validationFailures = QStringList(); + + if (this->nameInput->text().isEmpty()) { + validationFailures.emplace_back("Missing region name."); + } + + bool conversionOk = false; + + std::uint32_t startAddress = this->startAddressInput->text().toUInt(&conversionOk, 16); + if (!conversionOk) { + validationFailures.emplace_back("Invalid start address."); + } + + std::uint32_t endAddress = this->endAddressInput->text().toUInt(&conversionOk, 16); + if (!conversionOk) { + validationFailures.emplace_back("Invalid end address."); + } + + if (startAddress > endAddress) { + validationFailures.emplace_back("The start address exceeds the end address."); + } + + auto addressType = this->getSelectedAddressInputType(); + const auto memoryAddressRange = this->memoryDescriptor.addressRange; + + const auto memoryAddressRangeStr = QString( + "0x" + QString::number(memoryAddressRange.startAddress, 16).toUpper() + QString(" -> ") + + "0x" + QString::number(memoryAddressRange.endAddress, 16).toUpper() + ); + + const auto absoluteAddressRange = addressType == MemoryRegionAddressInputType::RELATIVE ? + this->convertRelativeToAbsoluteAddressRange( + TargetMemoryAddressRange(startAddress, endAddress) + ) : TargetMemoryAddressRange(startAddress, endAddress); + + if (absoluteAddressRange.startAddress < memoryAddressRange.startAddress + || absoluteAddressRange.startAddress > memoryAddressRange.endAddress + ) { + validationFailures.emplace_back( + "The start address is not within the absolute memory address range (" + memoryAddressRangeStr + ")." + ); + } + + if (absoluteAddressRange.endAddress < memoryAddressRange.startAddress + || absoluteAddressRange.endAddress > memoryAddressRange.endAddress + ) { + validationFailures.emplace_back( + "The end address not within the absolute memory address range (" + memoryAddressRangeStr + ")." + ); + } + + return validationFailures; } - if (startAddress > endAddress) { - validationFailures.emplace_back("The start address exceeds the end address."); + void RegionItem::initFormInputs() { + const auto& region = this->getMemoryRegion(); + + this->nameInput = this->formWidget->findChild("name-input"); + this->addressTypeInput = this->formWidget->findChild("address-type-input"); + this->startAddressInput = this->formWidget->findChild("start-address-input"); + this->endAddressInput = this->formWidget->findChild("end-address-input"); + this->sizeInput = this->formWidget->findChild("size-input"); + + this->nameInput->setText(region.name); + this->sizeInput->setText( + QString::number((region.addressRange.endAddress - region.addressRange.startAddress) + 1) + ); + + for (const auto& [optionName, option] : RegionItem::addressRangeTypeOptionsByName) { + this->addressTypeInput->addItem(option.text, optionName); + } + + if (region.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE) { + auto relativeAddressRange = this->convertAbsoluteToRelativeAddressRange(region.addressRange); + this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("relative").text); + + this->startAddressInput->setText( + "0x" + QString::number(relativeAddressRange.startAddress, 16).toUpper() + ); + this->endAddressInput->setText( + "0x" + QString::number(relativeAddressRange.endAddress, 16).toUpper() + ); + + } else { + this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("absolute").text); + + this->startAddressInput->setText( + "0x" + QString::number(region.addressRange.startAddress, 16).toUpper() + ); + this->endAddressInput->setText( + "0x" + QString::number(region.addressRange.endAddress, 16).toUpper() + ); + } + + QObject::connect(this->startAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange); + QObject::connect(this->endAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange); + QObject::connect(this->sizeInput, &QLineEdit::textEdited, this, &RegionItem::onSizeInputChange); + QObject::connect(this->nameInput, &QLineEdit::textEdited, this, &RegionItem::onNameInputChange); } - auto addressType = this->getSelectedAddressInputType(); - const auto memoryAddressRange = this->memoryDescriptor.addressRange; + MemoryRegionAddressInputType RegionItem::getSelectedAddressInputType() const { + auto selectedAddressTypeOptionName = this->addressTypeInput->currentData().toString(); + if (RegionItem::addressRangeTypeOptionsByName.contains(selectedAddressTypeOptionName)) { + return RegionItem::addressRangeTypeOptionsByName.at(selectedAddressTypeOptionName).addressType; + } - const auto memoryAddressRangeStr = QString( - "0x" + QString::number(memoryAddressRange.startAddress, 16).toUpper() + QString(" -> ") - + "0x" + QString::number(memoryAddressRange.endAddress, 16).toUpper() - ); + return MemoryRegionAddressInputType::ABSOLUTE; + } - const auto absoluteAddressRange = addressType == MemoryRegionAddressInputType::RELATIVE ? - this->convertRelativeToAbsoluteAddressRange(TargetMemoryAddressRange(startAddress, endAddress)) - : TargetMemoryAddressRange(startAddress, endAddress); - - if (absoluteAddressRange.startAddress < memoryAddressRange.startAddress - || absoluteAddressRange.startAddress > memoryAddressRange.endAddress - ) { - validationFailures.emplace_back( - "The start address is not within the absolute memory address range (" + memoryAddressRangeStr + ")." + TargetMemoryAddressRange RegionItem::convertAbsoluteToRelativeAddressRange( + const TargetMemoryAddressRange& absoluteAddressRange + ) const { + return TargetMemoryAddressRange( + absoluteAddressRange.startAddress - this->memoryDescriptor.addressRange.startAddress, + absoluteAddressRange.endAddress - this->memoryDescriptor.addressRange.startAddress ); } - if (absoluteAddressRange.endAddress < memoryAddressRange.startAddress - || absoluteAddressRange.endAddress > memoryAddressRange.endAddress - ) { - validationFailures.emplace_back( - "The end address not within the absolute memory address range (" + memoryAddressRangeStr + ")." + TargetMemoryAddressRange RegionItem::convertRelativeToAbsoluteAddressRange( + const TargetMemoryAddressRange& relativeAddressRange + ) const { + return TargetMemoryAddressRange( + relativeAddressRange.startAddress + this->memoryDescriptor.addressRange.startAddress, + relativeAddressRange.endAddress + this->memoryDescriptor.addressRange.startAddress ); } - return validationFailures; -} + void RegionItem::onAddressRangeInputChange() { + bool startAddressConversionOk = false; + bool endAddressConversionOk = false; + std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16); + std::uint32_t endAddress = this->endAddressInput->text().toUInt(&endAddressConversionOk, 16); -void RegionItem::initFormInputs() { - const auto& region = this->getMemoryRegion(); - - this->nameInput = this->formWidget->findChild("name-input"); - this->addressTypeInput = this->formWidget->findChild("address-type-input"); - this->startAddressInput = this->formWidget->findChild("start-address-input"); - this->endAddressInput = this->formWidget->findChild("end-address-input"); - this->sizeInput = this->formWidget->findChild("size-input"); - - this->nameInput->setText(region.name); - this->sizeInput->setText(QString::number((region.addressRange.endAddress - region.addressRange.startAddress) + 1)); - - for (const auto& [optionName, option] : RegionItem::addressRangeTypeOptionsByName) { - this->addressTypeInput->addItem(option.text, optionName); + if (startAddressConversionOk && endAddressConversionOk && startAddress <= endAddress) { + this->sizeInput->setText(QString::number((endAddress - startAddress) + 1)); + } } - if (region.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE) { - auto relativeAddressRange = this->convertAbsoluteToRelativeAddressRange(region.addressRange); - this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("relative").text); + void RegionItem::onSizeInputChange() { + bool startAddressConversionOk = false; + bool sizeConversionOk = false; + std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16); + std::uint32_t size = this->sizeInput->text().toUInt(&sizeConversionOk, 10); - this->startAddressInput->setText("0x" + QString::number(relativeAddressRange.startAddress, 16).toUpper()); - this->endAddressInput->setText("0x" + QString::number(relativeAddressRange.endAddress, 16).toUpper()); - - } else { - this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("absolute").text); - - this->startAddressInput->setText("0x" + QString::number(region.addressRange.startAddress, 16).toUpper()); - this->endAddressInput->setText("0x" + QString::number(region.addressRange.endAddress, 16).toUpper()); + if (startAddressConversionOk && sizeConversionOk && size > 0) { + this->endAddressInput->setText("0x" + QString::number((startAddress + size) - 1, 16).toUpper()); + } } - QObject::connect(this->startAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange); - QObject::connect(this->endAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange); - QObject::connect(this->sizeInput, &QLineEdit::textEdited, this, &RegionItem::onSizeInputChange); - QObject::connect(this->nameInput, &QLineEdit::textEdited, this, &RegionItem::onNameInputChange); -} - -MemoryRegionAddressInputType RegionItem::getSelectedAddressInputType() const { - auto selectedAddressTypeOptionName = this->addressTypeInput->currentData().toString(); - if (RegionItem::addressRangeTypeOptionsByName.contains(selectedAddressTypeOptionName)) { - return RegionItem::addressRangeTypeOptionsByName.at(selectedAddressTypeOptionName).addressType; - } - - return MemoryRegionAddressInputType::ABSOLUTE; -} - -TargetMemoryAddressRange RegionItem::convertAbsoluteToRelativeAddressRange( - const TargetMemoryAddressRange& absoluteAddressRange -) const { - return TargetMemoryAddressRange( - absoluteAddressRange.startAddress - this->memoryDescriptor.addressRange.startAddress, - absoluteAddressRange.endAddress - this->memoryDescriptor.addressRange.startAddress - ); -} - -TargetMemoryAddressRange RegionItem::convertRelativeToAbsoluteAddressRange( - const TargetMemoryAddressRange& relativeAddressRange -) const { - return TargetMemoryAddressRange( - relativeAddressRange.startAddress + this->memoryDescriptor.addressRange.startAddress, - relativeAddressRange.endAddress + this->memoryDescriptor.addressRange.startAddress - ); -} - -void RegionItem::onAddressRangeInputChange() { - bool startAddressConversionOk = false; - bool endAddressConversionOk = false; - std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16); - std::uint32_t endAddress = this->endAddressInput->text().toUInt(&endAddressConversionOk, 16); - - if (startAddressConversionOk && endAddressConversionOk && startAddress <= endAddress) { - this->sizeInput->setText(QString::number((endAddress - startAddress) + 1)); + void RegionItem::onNameInputChange() { + auto newName = this->nameInput->text(); + newName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH); + this->nameLabel->setText(newName); } } - -void RegionItem::onSizeInputChange() { - bool startAddressConversionOk = false; - bool sizeConversionOk = false; - std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16); - std::uint32_t size = this->sizeInput->text().toUInt(&sizeConversionOk, 10); - - if (startAddressConversionOk && sizeConversionOk && size > 0) { - this->endAddressInput->setText("0x" + QString::number((startAddress + size) - 1, 16).toUpper()); - } -} - -void RegionItem::onNameInputChange() { - auto newName = this->nameInput->text(); - newName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH); - this->nameLabel->setText(newName); -} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitBodyWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitBodyWidget.cpp index b02173f0..b1bd044d 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitBodyWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitBodyWidget.cpp @@ -4,75 +4,79 @@ #include #include -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + BitBodyWidget::BitBodyWidget( + int bitIndex, + std::bitset::digits>::reference bit, + bool readOnly, + QWidget* parent + ): ClickableWidget(parent), bitIndex(bitIndex), bit(bit), readOnly(readOnly) { + this->setFixedSize(BitBodyWidget::WIDTH, BitBodyWidget::HEIGHT); + this->setContentsMargins(0, 0, 0, 0); + } -BitBodyWidget::BitBodyWidget( - int bitIndex, - std::bitset::digits>::reference bit, - bool readOnly, - QWidget* parent -): ClickableWidget(parent), bitIndex(bitIndex), bit(bit), readOnly(readOnly) { - this->setFixedSize(BitBodyWidget::WIDTH, BitBodyWidget::HEIGHT); - this->setContentsMargins(0, 0, 0, 0); -} + bool BitBodyWidget::event(QEvent* event) { + if (this->isEnabled() && !this->readOnly) { + switch (event->type()) { + case QEvent::Enter: { + this->hoverActive = true; + this->update(); + break; + } + case QEvent::Leave: { + this->hoverActive = false; + this->update(); + break; + } + default: { + break; + } + } + } -bool BitBodyWidget::event(QEvent* event) { - if (this->isEnabled() && !this->readOnly) { - switch (event->type()) { - case QEvent::Enter: { - this->hoverActive = true; + return QWidget::event(event); + } + + void BitBodyWidget::mouseReleaseEvent(QMouseEvent* event) { + if (this->isEnabled()) { + if (!this->readOnly && event->button() == Qt::MouseButton::LeftButton) { + this->bit = !this->bit; this->update(); - break; - } - case QEvent::Leave: { - this->hoverActive = false; - this->update(); - break; - } - default: { - break; } + + ClickableWidget::mouseReleaseEvent(event); } } - return QWidget::event(event); -} + void BitBodyWidget::paintEvent(QPaintEvent* event) { + auto painter = QPainter(this); + this->drawWidget(painter); + } -void BitBodyWidget::mouseReleaseEvent(QMouseEvent* event) { - if (this->isEnabled()) { - if (!this->readOnly && event->button() == Qt::MouseButton::LeftButton) { - this->bit = !this->bit; - this->update(); + void BitBodyWidget::drawWidget(QPainter& painter) { + painter.setRenderHints( + QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, + true + ); + + auto bodyColor = QColor(this->bit == true ? "#7B5E38" : "#908D85"); + + if (!this->isEnabled()) { + bodyColor.setAlpha(100); + + } else if (!this->hoverActive) { + bodyColor.setAlpha(235); } - ClickableWidget::mouseReleaseEvent(event); + painter.setPen(Qt::PenStyle::NoPen); + painter.setBrush(bodyColor); + + painter.drawRect( + 0, + 0, + BitBodyWidget::WIDTH, + BitBodyWidget::HEIGHT + ); } } - -void BitBodyWidget::paintEvent(QPaintEvent* event) { - auto painter = QPainter(this); - this->drawWidget(painter); -} - -void BitBodyWidget::drawWidget(QPainter& painter) { - painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true); - - auto bodyColor = QColor(this->bit == true ? "#7B5E38" : "#908D85"); - - if (!this->isEnabled()) { - bodyColor.setAlpha(100); - - } else if (!this->hoverActive) { - bodyColor.setAlpha(235); - } - - painter.setPen(Qt::PenStyle::NoPen); - painter.setBrush(bodyColor); - - painter.drawRect( - 0, - 0, - BitBodyWidget::WIDTH, - BitBodyWidget::HEIGHT - ); -} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitWidget.cpp index 8d9b57ee..563c729a 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitWidget.cpp @@ -6,50 +6,51 @@ #include "BitBodyWidget.hpp" -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + BitWidget::BitWidget( + int bitIndex, + int bitNumber, + std::bitset::digits>& bitset, + bool readOnly, + QWidget* parent + ): QWidget(parent), bitIndex(bitIndex), bitNumber(bitNumber), bitset(bitset), readOnly(readOnly) { + this->setFixedSize(BitWidget::WIDTH, BitWidget::HEIGHT); -BitWidget::BitWidget( - int bitIndex, - int bitNumber, - std::bitset::digits>& bitset, - bool readOnly, - QWidget* parent -): QWidget(parent), bitIndex(bitIndex), bitNumber(bitNumber), bitset(bitset), readOnly(readOnly) { - this->setFixedSize(BitWidget::WIDTH, BitWidget::HEIGHT); + auto* layout = new QVBoxLayout(this); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + layout->setAlignment(Qt::AlignmentFlag::AlignTop); - auto* layout = new QVBoxLayout(this); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - layout->setAlignment(Qt::AlignmentFlag::AlignTop); + this->body = new BitBodyWidget( + this->bitIndex, + this->bitset[static_cast(this->bitIndex)], + this->readOnly, + this + ); - this->body = new BitBodyWidget( - this->bitIndex, - this->bitset[static_cast(this->bitIndex)], - this->readOnly, - this - ); + this->bitLabel = new QLabel("Bit", this); + this->bitNumberLabel = new QLabel(QString::number(this->bitNumber), this); - this->bitLabel = new QLabel("Bit", this); - this->bitNumberLabel = new QLabel(QString::number(this->bitNumber), this); + this->bitLabel->setObjectName("register-bit-label"); + this->bitNumberLabel->setObjectName("register-bit-number-label"); - this->bitLabel->setObjectName("register-bit-label"); - this->bitNumberLabel->setObjectName("register-bit-number-label"); + this->bitLabel->setFixedHeight(BitWidget::LABEL_HEIGHT); + this->bitNumberLabel->setFixedHeight(BitWidget::LABEL_HEIGHT); - this->bitLabel->setFixedHeight(BitWidget::LABEL_HEIGHT); - this->bitNumberLabel->setFixedHeight(BitWidget::LABEL_HEIGHT); + this->bitLabel->setAlignment(Qt::AlignmentFlag::AlignCenter); + this->bitNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter); - this->bitLabel->setAlignment(Qt::AlignmentFlag::AlignCenter); - this->bitNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter); + layout->addWidget(this->bitLabel); + layout->addWidget(this->bitNumberLabel); + layout->addSpacing(BitWidget::VERTICAL_SPACING); + layout->addWidget(this->body); + layout->addStretch(1); - layout->addWidget(this->bitLabel); - layout->addWidget(this->bitNumberLabel); - layout->addSpacing(BitWidget::VERTICAL_SPACING); - layout->addWidget(this->body); - layout->addStretch(1); - - if (!this->readOnly) { - QObject::connect(this->body, &BitBodyWidget::clicked, this, [this] { - emit this->bitChanged(); - }); + if (!this->readOnly) { + QObject::connect(this->body, &BitBodyWidget::clicked, this, [this] { + emit this->bitChanged(); + }); + } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitsetWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitsetWidget.cpp index 03529b80..1580f30a 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitsetWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitsetWidget.cpp @@ -8,101 +8,102 @@ #include "../TargetRegisterInspectorWindow.hpp" -using namespace Bloom::Widgets; - -BitsetWidget::BitsetWidget(int byteNumber, unsigned char& byte, bool readOnly, QWidget* parent): -QWidget(parent), byteNumber(byteNumber), byte(byte), readOnly(readOnly) { - this->setObjectName("bitset-widget"); - auto* bitLayout = new QHBoxLayout(this); - bitLayout->setSpacing(BitWidget::SPACING); - bitLayout->setContentsMargins(0, 0, 0, 0); - this->setContentsMargins(0, 0, 0, 0); - this->setFixedSize( - static_cast((BitWidget::WIDTH + BitWidget::SPACING) * this->bitset.size() - BitWidget::SPACING), - BitsetWidget::HEIGHT - ); - - for (int bitIndex = (std::numeric_limits::digits - 1); bitIndex >= 0; bitIndex--) { - auto* bitWidget = new BitWidget( - bitIndex, - (this->byteNumber * 8) + bitIndex, - this->bitset, - this->readOnly, - this +namespace Bloom::Widgets +{ + BitsetWidget::BitsetWidget(int byteNumber, unsigned char& byte, bool readOnly, QWidget* parent) + : QWidget(parent), byteNumber(byteNumber), byte(byte), readOnly(readOnly) { + this->setObjectName("bitset-widget"); + auto* bitLayout = new QHBoxLayout(this); + bitLayout->setSpacing(BitWidget::SPACING); + bitLayout->setContentsMargins(0, 0, 0, 0); + this->setContentsMargins(0, 0, 0, 0); + this->setFixedSize( + static_cast((BitWidget::WIDTH + BitWidget::SPACING) * this->bitset.size() - BitWidget::SPACING), + BitsetWidget::HEIGHT ); - bitLayout->addWidget(bitWidget, 0, Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop); - QObject::connect( - bitWidget, - &BitWidget::bitChanged, - this, - [this] { - this->byte = static_cast(this->bitset.to_ulong()); - this->repaint(); - emit this->byteChanged(); - } + for (int bitIndex = (std::numeric_limits::digits - 1); bitIndex >= 0; bitIndex--) { + auto* bitWidget = new BitWidget( + bitIndex, + (this->byteNumber * 8) + bitIndex, + this->bitset, + this->readOnly, + this + ); + + bitLayout->addWidget(bitWidget, 0, Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop); + QObject::connect( + bitWidget, + &BitWidget::bitChanged, + this, + [this] { + this->byte = static_cast(this->bitset.to_ulong()); + this->repaint(); + emit this->byteChanged(); + } + ); + } + } + + void BitsetWidget::updateValue() { + this->bitset = {this->byte}; + this->update(); + } + + void BitsetWidget::paintEvent(QPaintEvent* event) { + QWidget::paintEvent(event); + auto painter = QPainter(this); + this->drawWidget(painter); + } + + void BitsetWidget::drawWidget(QPainter& painter) { + auto byteHex = "0x" + QString::number(this->byte, 16).toUpper(); + + painter.setPen(QColor("#474747")); + constexpr int labelHeight = 11; + int containerWidth = this->width(); + constexpr int charWidth = 6; + auto labelWidth = static_cast(charWidth * byteHex.size()) + 13; + auto width = (containerWidth - (BitWidget::WIDTH) - labelWidth) / 2; + + painter.drawLine(QLine( + BitWidget::WIDTH / 2, + BitWidget::HEIGHT, + BitWidget::WIDTH / 2, + BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT + )); + + painter.drawLine(QLine( + containerWidth - (BitWidget::WIDTH / 2) - 1, + BitWidget::HEIGHT, + containerWidth - (BitWidget::WIDTH / 2) - 1, + BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT + )); + + painter.drawLine(QLine( + BitWidget::WIDTH / 2, + BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT, + static_cast((BitWidget::WIDTH / 2) + width), + BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT + )); + + painter.drawLine(QLine( + static_cast((BitWidget::WIDTH / 2) + width + labelWidth), + BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT, + containerWidth - (BitWidget::WIDTH / 2) - 1, + BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT + )); + + painter.setPen(QColor("#8a8a8d")); + painter.drawText( + QRect( + static_cast((BitWidget::WIDTH / 2) + width), + BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - (labelHeight / 2), + labelWidth, + labelHeight + ), + Qt::AlignCenter, + byteHex ); } } - -void BitsetWidget::updateValue() { - this->bitset = {this->byte}; - this->update(); -} - -void BitsetWidget::paintEvent(QPaintEvent* event) { - QWidget::paintEvent(event); - auto painter = QPainter(this); - this->drawWidget(painter); -} - -void BitsetWidget::drawWidget(QPainter& painter) { - auto byteHex = "0x" + QString::number(this->byte, 16).toUpper(); - - painter.setPen(QColor("#474747")); - constexpr int labelHeight = 11; - int containerWidth = this->width(); - constexpr int charWidth = 6; - auto labelWidth = static_cast(charWidth * byteHex.size()) + 13; - auto width = (containerWidth - (BitWidget::WIDTH) - labelWidth) / 2; - - painter.drawLine(QLine( - BitWidget::WIDTH / 2, - BitWidget::HEIGHT, - BitWidget::WIDTH / 2, - BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - )); - - painter.drawLine(QLine( - containerWidth - (BitWidget::WIDTH / 2) - 1, - BitWidget::HEIGHT, - containerWidth - (BitWidget::WIDTH / 2) - 1, - BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - )); - - painter.drawLine(QLine( - BitWidget::WIDTH / 2, - BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT, - static_cast((BitWidget::WIDTH / 2) + width), - BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - )); - - painter.drawLine(QLine( - static_cast((BitWidget::WIDTH / 2) + width + labelWidth), - BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT, - containerWidth - (BitWidget::WIDTH / 2) - 1, - BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - )); - - painter.setPen(QColor("#8a8a8d")); - painter.drawText( - QRect( - static_cast((BitWidget::WIDTH / 2) + width), - BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - (labelHeight / 2), - labelWidth, - labelHeight - ), - Qt::AlignCenter, - byteHex - ); -} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/CurrentItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/CurrentItem.cpp index 2e91594f..0682742b 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/CurrentItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/CurrentItem.cpp @@ -3,15 +3,16 @@ #include #include -using namespace Bloom::Widgets; - -CurrentItem::CurrentItem( - const Targets::TargetMemoryBuffer& registerValue, - QWidget* parent -): Item(registerValue, parent) { - this->setObjectName("current-item"); - this->setFixedHeight(30); - this->titleLabel->setText("Current value"); - this->layout->addWidget(titleLabel, 0, Qt::AlignmentFlag::AlignLeft); - this->layout->setContentsMargins(5, 0, 5, 0); +namespace Bloom::Widgets +{ + CurrentItem::CurrentItem( + const Targets::TargetMemoryBuffer& registerValue, + QWidget* parent + ): Item(registerValue, parent) { + this->setObjectName("current-item"); + this->setFixedHeight(30); + this->titleLabel->setText("Current value"); + this->layout->addWidget(titleLabel, 0, Qt::AlignmentFlag::AlignLeft); + this->layout->setContentsMargins(5, 0, 5, 0); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/Item.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/Item.cpp index 56732ae2..70357b40 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/Item.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/Item.cpp @@ -2,26 +2,27 @@ #include -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + Item::Item(const Targets::TargetMemoryBuffer& registerValue, QWidget* parent) + : ClickableWidget(parent), registerValue(registerValue) { + auto onClick = [this] { + this->setSelected(true); + }; -Item::Item(const Targets::TargetMemoryBuffer& registerValue, QWidget* parent): -ClickableWidget(parent), registerValue(registerValue) { - auto onClick = [this] { - this->setSelected(true); - }; + QObject::connect(this, &ClickableWidget::clicked, this, onClick); + QObject::connect(this, &ClickableWidget::rightClicked, this, onClick); - QObject::connect(this, &ClickableWidget::clicked, this, onClick); - QObject::connect(this, &ClickableWidget::rightClicked, this, onClick); + this->setSelected(false); + } - this->setSelected(false); -} + void Item::setSelected(bool selected) { + this->setProperty("selected", selected); + this->style()->unpolish(this); + this->style()->polish(this); -void Item::setSelected(bool selected) { - this->setProperty("selected", selected); - this->style()->unpolish(this); - this->style()->polish(this); - - if (selected) { - emit this->selected(this); + if (selected) { + emit this->selected(this); + } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/RegisterHistoryItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/RegisterHistoryItem.cpp index 1ba63b12..5eb11cf7 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/RegisterHistoryItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/RegisterHistoryItem.cpp @@ -4,38 +4,39 @@ #include #include -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + RegisterHistoryItem::RegisterHistoryItem( + const Targets::TargetMemoryBuffer& registerValue, + const QDateTime& changeDate, + QWidget* parent + ): Item(registerValue, parent) { + this->setObjectName("register-history-item"); + this->setFixedHeight(50); + this->layout->setContentsMargins(5, 8, 5, 0); -RegisterHistoryItem::RegisterHistoryItem( - const Targets::TargetMemoryBuffer& registerValue, - const QDateTime& changeDate, - QWidget* parent -): Item(registerValue, parent) { - this->setObjectName("register-history-item"); - this->setFixedHeight(50); - this->layout->setContentsMargins(5, 8, 5, 0); + this->dateLabel->setText(changeDate.toString("dd/MM/yyyy hh:mm:ss")); + this->dateLabel->setObjectName("date-label"); - this->dateLabel->setText(changeDate.toString("dd/MM/yyyy hh:mm:ss")); - this->dateLabel->setObjectName("date-label"); + this->valueLabel->setText("0x" + QString(QByteArray( + reinterpret_cast(registerValue.data()), + static_cast(registerValue.size()) + ).toHex()).toUpper()); + this->valueLabel->setObjectName("value-label"); - this->valueLabel->setText("0x" + QString(QByteArray( - reinterpret_cast(registerValue.data()), - static_cast(registerValue.size()) - ).toHex()).toUpper()); - this->valueLabel->setObjectName("value-label"); + this->descriptionLabel->setText("Register Written"); + this->descriptionLabel->setObjectName("description-label"); - this->descriptionLabel->setText("Register Written"); - this->descriptionLabel->setObjectName("description-label"); + auto* subLabelLayout = new QHBoxLayout(); + subLabelLayout->setSpacing(0); + subLabelLayout->setContentsMargins(0, 0, 0, 0); + subLabelLayout->addWidget(this->valueLabel, 0, Qt::AlignmentFlag::AlignLeft); + subLabelLayout->addStretch(1); + subLabelLayout->addWidget(this->descriptionLabel, 0, Qt::AlignmentFlag::AlignRight); - auto* subLabelLayout = new QHBoxLayout(); - subLabelLayout->setSpacing(0); - subLabelLayout->setContentsMargins(0, 0, 0, 0); - subLabelLayout->addWidget(this->valueLabel, 0, Qt::AlignmentFlag::AlignLeft); - subLabelLayout->addStretch(1); - subLabelLayout->addWidget(this->descriptionLabel, 0, Qt::AlignmentFlag::AlignRight); - - this->layout->setSpacing(5); - this->layout->addWidget(this->dateLabel, 0, Qt::AlignmentFlag::AlignTop); - this->layout->addLayout(subLabelLayout); - this->layout->addStretch(1); + this->layout->setSpacing(5); + this->layout->addWidget(this->dateLabel, 0, Qt::AlignmentFlag::AlignTop); + this->layout->addLayout(subLabelLayout); + this->layout->addStretch(1); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/RegisterHistoryWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/RegisterHistoryWidget.cpp index 572415fb..774556c9 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/RegisterHistoryWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/RegisterHistoryWidget.cpp @@ -12,125 +12,130 @@ #include "src/Helpers/DateTime.hpp" #include "src/Exceptions/Exception.hpp" -using namespace Bloom::Widgets; -using namespace Bloom::Exceptions; +namespace Bloom::Widgets +{ + using namespace Bloom::Exceptions; -using Bloom::Targets::TargetRegisterDescriptor; -using Bloom::Targets::TargetRegisterDescriptors; -using Bloom::Targets::TargetRegisterType; + using Bloom::Targets::TargetRegisterDescriptor; + using Bloom::Targets::TargetRegisterDescriptors; + using Bloom::Targets::TargetRegisterType; -RegisterHistoryWidget::RegisterHistoryWidget( - const Targets::TargetRegisterDescriptor& registerDescriptor, - const Targets::TargetMemoryBuffer& currentValue, - InsightWorker& insightWorker, - QWidget* parent -): QWidget(parent), registerDescriptor(registerDescriptor), insightWorker(insightWorker) { - this->setObjectName("target-register-history-widget"); - this->setFixedWidth(300); + RegisterHistoryWidget::RegisterHistoryWidget( + const Targets::TargetRegisterDescriptor& registerDescriptor, + const Targets::TargetMemoryBuffer& currentValue, + InsightWorker& insightWorker, + QWidget* parent + ): QWidget(parent), registerDescriptor(registerDescriptor), insightWorker(insightWorker) { + this->setObjectName("target-register-history-widget"); + this->setFixedWidth(300); - auto widgetUiFile = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget" - + "/UiFiles/RegisterHistoryWidget.ui" - ) - ); + auto widgetUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget" + + "/UiFiles/RegisterHistoryWidget.ui" + ) + ); - if (!widgetUiFile.open(QFile::ReadOnly)) { - throw Exception("Failed to open RegisterHistoryWidget UI file"); - } - - auto uiLoader = UiLoader(this); - this->container = uiLoader.load(&widgetUiFile, this); - this->container->setFixedSize(this->size()); - this->container->setContentsMargins(1, 1, 1, 1); - - this->itemContainer = this->container->findChild("item-container"); - this->itemContainerLayout = this->itemContainer->findChild("item-container-layout"); - auto titleBar = this->container->findChild("title-bar"); - auto title = titleBar->findChild("title"); - - titleBar->setContentsMargins(0, 0, 0, 0); - title->setFixedHeight(titleBar->height()); - - QObject::connect( - &insightWorker, - &InsightWorker::targetStateUpdated, - this, - &RegisterHistoryWidget::onTargetStateChanged - ); - - QObject::connect( - &insightWorker, - &InsightWorker::targetRegistersWritten, - this, - &RegisterHistoryWidget::onRegistersWritten - ); - - this->currentItem = new CurrentItem(currentValue, this); - QObject::connect(this->currentItem, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange); - this->itemContainerLayout->addWidget(this->currentItem); - this->currentItem->setSelected(true); - - auto* separatorWidget = new QWidget(this); - auto* separatorLayout = new QHBoxLayout(separatorWidget); - auto* separatorLabel = new QLabel("Select an item to restore", separatorWidget); - separatorWidget->setFixedHeight(40); - separatorWidget->setObjectName("separator-widget"); - separatorLayout->setContentsMargins(0, 10, 0, 10); - separatorLabel->setObjectName("separator-label"); - separatorLayout->addWidget(separatorLabel, 0, Qt::AlignmentFlag::AlignHCenter); - this->itemContainerLayout->addWidget(separatorWidget); - - this->show(); -} - -void RegisterHistoryWidget::updateCurrentItemValue(const Targets::TargetMemoryBuffer& registerValue) { - this->currentItem->registerValue = registerValue; - - if (this->selectedItemWidget != nullptr && this->currentItem == this->selectedItemWidget) { - this->selectCurrentItem(); - } -} - -void RegisterHistoryWidget::selectCurrentItem() { - this->currentItem->setSelected(true); -} - -void RegisterHistoryWidget::addItem(const Targets::TargetMemoryBuffer& registerValue, const QDateTime& changeDate) { - auto* item = new RegisterHistoryItem(registerValue, changeDate, this->itemContainer); - QObject::connect(item, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange); - this->itemContainerLayout->insertWidget(2, item); -} - -void RegisterHistoryWidget::resizeEvent(QResizeEvent* event) { - this->container->setFixedSize( - this->width(), - this->height() - ); -} - -void RegisterHistoryWidget::onTargetStateChanged(Targets::TargetState newState) { - using Targets::TargetState; - this->targetState = newState; -} - -void RegisterHistoryWidget::onItemSelectionChange(Item* newlySelectedWidget) { - if (this->selectedItemWidget != newlySelectedWidget) { - if (this->selectedItemWidget != nullptr) { - this->selectedItemWidget->setSelected(false); + if (!widgetUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open RegisterHistoryWidget UI file"); } - this->selectedItemWidget = newlySelectedWidget; + auto uiLoader = UiLoader(this); + this->container = uiLoader.load(&widgetUiFile, this); + this->container->setFixedSize(this->size()); + this->container->setContentsMargins(1, 1, 1, 1); + + this->itemContainer = this->container->findChild("item-container"); + this->itemContainerLayout = this->itemContainer->findChild("item-container-layout"); + auto titleBar = this->container->findChild("title-bar"); + auto title = titleBar->findChild("title"); + + titleBar->setContentsMargins(0, 0, 0, 0); + title->setFixedHeight(titleBar->height()); + + QObject::connect( + &insightWorker, + &InsightWorker::targetStateUpdated, + this, + &RegisterHistoryWidget::onTargetStateChanged + ); + + QObject::connect( + &insightWorker, + &InsightWorker::targetRegistersWritten, + this, + &RegisterHistoryWidget::onRegistersWritten + ); + + this->currentItem = new CurrentItem(currentValue, this); + QObject::connect(this->currentItem, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange); + this->itemContainerLayout->addWidget(this->currentItem); + this->currentItem->setSelected(true); + + auto* separatorWidget = new QWidget(this); + auto* separatorLayout = new QHBoxLayout(separatorWidget); + auto* separatorLabel = new QLabel("Select an item to restore", separatorWidget); + separatorWidget->setFixedHeight(40); + separatorWidget->setObjectName("separator-widget"); + separatorLayout->setContentsMargins(0, 10, 0, 10); + separatorLabel->setObjectName("separator-label"); + separatorLayout->addWidget(separatorLabel, 0, Qt::AlignmentFlag::AlignHCenter); + this->itemContainerLayout->addWidget(separatorWidget); + + this->show(); } - emit this->historyItemSelected(newlySelectedWidget->registerValue); -} + void RegisterHistoryWidget::updateCurrentItemValue(const Targets::TargetMemoryBuffer& registerValue) { + this->currentItem->registerValue = registerValue; -void RegisterHistoryWidget::onRegistersWritten(Targets::TargetRegisters targetRegisters, const QDateTime& changeDate) { - for (const auto& targetRegister : targetRegisters) { - if (targetRegister.descriptor == this->registerDescriptor) { - this->addItem(targetRegister.value, changeDate); - this->updateCurrentItemValue(targetRegister.value); + if (this->selectedItemWidget != nullptr && this->currentItem == this->selectedItemWidget) { + this->selectCurrentItem(); + } + } + + void RegisterHistoryWidget::selectCurrentItem() { + this->currentItem->setSelected(true); + } + + void RegisterHistoryWidget::addItem(const Targets::TargetMemoryBuffer& registerValue, const QDateTime& changeDate) { + auto* item = new RegisterHistoryItem(registerValue, changeDate, this->itemContainer); + QObject::connect(item, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange); + this->itemContainerLayout->insertWidget(2, item); + } + + void RegisterHistoryWidget::resizeEvent(QResizeEvent* event) { + this->container->setFixedSize( + this->width(), + this->height() + ); + } + + void RegisterHistoryWidget::onTargetStateChanged(Targets::TargetState newState) { + using Targets::TargetState; + this->targetState = newState; + } + + void RegisterHistoryWidget::onItemSelectionChange(Item* newlySelectedWidget) { + if (this->selectedItemWidget != newlySelectedWidget) { + if (this->selectedItemWidget != nullptr) { + this->selectedItemWidget->setSelected(false); + } + + this->selectedItemWidget = newlySelectedWidget; + } + + emit this->historyItemSelected(newlySelectedWidget->registerValue); + } + + void RegisterHistoryWidget::onRegistersWritten( + Targets::TargetRegisters targetRegisters, + const QDateTime& changeDate + ) { + for (const auto& targetRegister : targetRegisters) { + if (targetRegister.descriptor == this->registerDescriptor) { + this->addItem(targetRegister.value, changeDate); + this->updateCurrentItemValue(targetRegister.value); + } } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/TargetRegisterInspectorWindow.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/TargetRegisterInspectorWindow.cpp index 0b10c3f6..42d2e723 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/TargetRegisterInspectorWindow.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/TargetRegisterInspectorWindow.cpp @@ -14,360 +14,380 @@ #include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp" #include "src/Insight/InsightWorker/Tasks/WriteTargetRegister.hpp" -using namespace Bloom::Widgets; - -using Bloom::Exceptions::Exception; -using Bloom::Targets::TargetRegisterDescriptor; -using Bloom::Targets::TargetRegisterDescriptors; -using Bloom::Targets::TargetRegisterType; -using Bloom::Targets::TargetState; - -TargetRegisterInspectorWindow::TargetRegisterInspectorWindow( - const Targets::TargetRegisterDescriptor& registerDescriptor, - InsightWorker& insightWorker, - TargetState currentTargetState, - std::optional registerValue, - QWidget* parent -): - QWidget(parent), - registerDescriptor(registerDescriptor), - insightWorker(insightWorker), - registerValue(registerValue.value_or(Targets::TargetMemoryBuffer(registerDescriptor.size, 0))) +namespace Bloom::Widgets { - this->setWindowFlag(Qt::Window); - auto registerName = QString::fromStdString(this->registerDescriptor.name.value()).toUpper(); - this->setObjectName("target-register-inspector-window"); - this->setWindowTitle("Inspect Register"); + using Bloom::Exceptions::Exception; + using Bloom::Targets::TargetRegisterDescriptor; + using Bloom::Targets::TargetRegisterDescriptors; + using Bloom::Targets::TargetRegisterType; + using Bloom::Targets::TargetState; - auto windowUiFile = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/UiFiles/TargetRegisterInspectorWindow.ui" - ) - ); + TargetRegisterInspectorWindow::TargetRegisterInspectorWindow( + const Targets::TargetRegisterDescriptor& registerDescriptor, + InsightWorker& insightWorker, + TargetState currentTargetState, + const std::optional& registerValue, + QWidget* parent + ) + : QWidget(parent) + , registerDescriptor(registerDescriptor) + , insightWorker(insightWorker) + , registerValue(registerValue.value_or(Targets::TargetMemoryBuffer(registerDescriptor.size, 0))) + { + this->setWindowFlag(Qt::Window); + auto registerName = QString::fromStdString(this->registerDescriptor.name.value()).toUpper(); + this->setObjectName("target-register-inspector-window"); + this->setWindowTitle("Inspect Register"); - auto windowStylesheet = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/Stylesheets/TargetRegisterInspectorWindow.qss" - ) - ); - - if (!windowUiFile.open(QFile::ReadOnly)) { - throw Exception("Failed to open TargetRegisterInspectorWindow UI file"); - } - - if (!windowStylesheet.open(QFile::ReadOnly)) { - throw Exception("Failed to open TargetRegisterInspectorWindow stylesheet file"); - } - - auto windowSize = QSize( - 840, - static_cast( - 440 - + ((BitsetWidget::HEIGHT + 20) * std::ceil(static_cast(this->registerValue.size()) / 2)) - + (!this->registerDescriptor.writable ? 50 : 0) - ) - ); - auto containerMargins = QMargins(15, 30, 15, 10); - - this->setStyleSheet(windowStylesheet.readAll()); - this->setFixedSize(windowSize); - - auto uiLoader = UiLoader(this); - this->container = uiLoader.load(&windowUiFile, this); - - this->container->setFixedSize(this->size()); - this->container->setContentsMargins(containerMargins); - - this->registerNameLabel = this->container->findChild("register-name"); - this->registerDescriptionLabel = this->container->findChild("register-description"); - this->contentContainer = this->container->findChild("content-container"); - this->registerValueContainer = this->container->findChild("register-value-container"); - this->registerValueTextInput = this->container->findChild("register-value-text-input"); - this->registerValueBitsetWidgetContainer = this->container->findChild("register-value-bitset-widget-container"); - this->refreshValueButton = this->container->findChild("refresh-value-btn"); - this->applyButton = this->container->findChild("apply-btn"); - this->helpButton = this->container->findChild("help-btn"); - this->closeButton = this->container->findChild("close-btn"); - - this->registerNameLabel->setText(registerName); - - if (this->registerDescriptor.description.has_value()) { - this->registerDescriptionLabel->setText(QString::fromStdString(this->registerDescriptor.description.value())); - this->registerDescriptionLabel->setVisible(true); - } - - this->registerHistoryWidget = new RegisterHistoryWidget( - this->registerDescriptor, - this->registerValue, - insightWorker, - this->container - ); - - auto* contentLayout = this->container->findChild("content-layout"); - contentLayout->insertWidget(0, this->registerHistoryWidget, 0, Qt::AlignmentFlag::AlignTop); - - auto* registerDetailsContainer = this->container->findChild("register-details-container"); - auto* registerValueContainer = this->container->findChild("register-value-container"); - registerValueContainer->setContentsMargins(15, 15, 15, 15); - registerDetailsContainer->setContentsMargins(15, 15, 15, 15); - - auto* registerDetailsNameInput = registerDetailsContainer->findChild("register-details-name-input"); - auto* registerDetailsSizeInput = registerDetailsContainer->findChild("register-details-size-input"); - auto* registerDetailsStartAddressInput = registerDetailsContainer->findChild( - "register-details-start-address-input" - ); - registerDetailsNameInput->setText(registerName); - registerDetailsSizeInput->setText(QString::number(this->registerDescriptor.size)); - registerDetailsStartAddressInput->setText( - "0x" + QString::number(this->registerDescriptor.startAddress.value(), 16).toUpper() - ); - - this->registerValueTextInput->setFixedWidth(BitsetWidget::WIDTH * 2); - - if (!this->registerDescriptor.writable) { - this->registerValueTextInput->setDisabled(true); - this->applyButton->setVisible(false); - - auto* readOnlyIndicatorLabel = this->registerValueContainer->findChild("read-only-indicator-label"); - readOnlyIndicatorLabel->show(); - } - - auto* registerBitsetWidgetLayout = this->registerValueBitsetWidgetContainer->findChild( - "register-value-bitset-widget-layout" - ); - - /* - * Each row of the BitsetWidget container should hold two BitsetWidgets. So we have a horizontal layout nested - * within a vertical layout. - */ - auto* bitsetSingleHorizontalLayout = new QHBoxLayout(); - bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING); - bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0); - - // The register value will be in MSB, which is OK for us as we present the bit widgets in MSB. - auto byteNumber = static_cast(this->registerValue.size() - 1); - for (std::uint32_t registerByteIndex = 0; registerByteIndex < this->registerValue.size(); registerByteIndex++) { - auto* bitsetWidget = new BitsetWidget( - byteNumber, - this->registerValue.at(registerByteIndex), - !this->registerDescriptor.writable, - this + auto windowUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/UiFiles/" + "TargetRegisterInspectorWindow.ui" + ) ); - bitsetSingleHorizontalLayout->addWidget(bitsetWidget, 0, Qt::AlignmentFlag::AlignLeft); + auto windowStylesheet = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/Stylesheets/" + "TargetRegisterInspectorWindow.qss" + ) + ); + + if (!windowUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open TargetRegisterInspectorWindow UI file"); + } + + if (!windowStylesheet.open(QFile::ReadOnly)) { + throw Exception("Failed to open TargetRegisterInspectorWindow stylesheet file"); + } + + auto windowSize = QSize( + 840, + static_cast( + 440 + + ((BitsetWidget::HEIGHT + 20) * std::ceil(static_cast(this->registerValue.size()) / 2)) + + (!this->registerDescriptor.writable ? 50 : 0) + ) + ); + auto containerMargins = QMargins(15, 30, 15, 10); + + this->setStyleSheet(windowStylesheet.readAll()); + this->setFixedSize(windowSize); + + auto uiLoader = UiLoader(this); + this->container = uiLoader.load(&windowUiFile, this); + + this->container->setFixedSize(this->size()); + this->container->setContentsMargins(containerMargins); + + this->registerNameLabel = this->container->findChild("register-name"); + this->registerDescriptionLabel = this->container->findChild("register-description"); + this->contentContainer = this->container->findChild("content-container"); + this->registerValueContainer = this->container->findChild("register-value-container"); + this->registerValueTextInput = this->container->findChild("register-value-text-input"); + this->registerValueBitsetWidgetContainer = this->container->findChild( + "register-value-bitset-widget-container" + ); + this->refreshValueButton = this->container->findChild("refresh-value-btn"); + this->applyButton = this->container->findChild("apply-btn"); + this->helpButton = this->container->findChild("help-btn"); + this->closeButton = this->container->findChild("close-btn"); + + this->registerNameLabel->setText(registerName); + + if (this->registerDescriptor.description.has_value()) { + this->registerDescriptionLabel->setText( + QString::fromStdString(this->registerDescriptor.description.value()) + ); + this->registerDescriptionLabel->setVisible(true); + } + + this->registerHistoryWidget = new RegisterHistoryWidget( + this->registerDescriptor, + this->registerValue, + insightWorker, + this->container + ); + + auto* contentLayout = this->container->findChild("content-layout"); + contentLayout->insertWidget(0, this->registerHistoryWidget, 0, Qt::AlignmentFlag::AlignTop); + + auto* registerDetailsContainer = this->container->findChild("register-details-container"); + auto* registerValueContainer = this->container->findChild("register-value-container"); + registerValueContainer->setContentsMargins(15, 15, 15, 15); + registerDetailsContainer->setContentsMargins(15, 15, 15, 15); + + auto* registerDetailsNameInput = registerDetailsContainer->findChild( + "register-details-name-input" + ); + auto* registerDetailsSizeInput = registerDetailsContainer->findChild( + "register-details-size-input" + ); + auto* registerDetailsStartAddressInput = registerDetailsContainer->findChild( + "register-details-start-address-input" + ); + registerDetailsNameInput->setText(registerName); + registerDetailsSizeInput->setText(QString::number(this->registerDescriptor.size)); + registerDetailsStartAddressInput->setText( + "0x" + QString::number(this->registerDescriptor.startAddress.value(), 16).toUpper() + ); + + this->registerValueTextInput->setFixedWidth(BitsetWidget::WIDTH * 2); + + if (!this->registerDescriptor.writable) { + this->registerValueTextInput->setDisabled(true); + this->applyButton->setVisible(false); + + auto* readOnlyIndicatorLabel = this->registerValueContainer->findChild( + "read-only-indicator-label" + ); + readOnlyIndicatorLabel->show(); + } + + auto* registerBitsetWidgetLayout = this->registerValueBitsetWidgetContainer->findChild( + "register-value-bitset-widget-layout" + ); + + /* + * Each row of the BitsetWidget container should hold two BitsetWidgets. So we have a horizontal layout nested + * within a vertical layout. + */ + auto* bitsetSingleHorizontalLayout = new QHBoxLayout(); + bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING); + bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0); + + // The register value will be in MSB, which is OK for us as we present the bit widgets in MSB. + auto byteNumber = static_cast(this->registerValue.size() - 1); + for (std::uint32_t registerByteIndex = 0; registerByteIndex < this->registerValue.size(); registerByteIndex++) { + auto* bitsetWidget = new BitsetWidget( + byteNumber, + this->registerValue.at(registerByteIndex), + !this->registerDescriptor.writable, + this + ); + + bitsetSingleHorizontalLayout->addWidget(bitsetWidget, 0, Qt::AlignmentFlag::AlignLeft); + QObject::connect( + bitsetWidget, + &BitsetWidget::byteChanged, + this, + &TargetRegisterInspectorWindow::updateRegisterValueInputField + ); + this->bitsetWidgets.push_back(bitsetWidget); + + if (((registerByteIndex + 1) % 2) == 0) { + registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout); + bitsetSingleHorizontalLayout = new QHBoxLayout(); + bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING); + bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0); + } + + byteNumber--; + } + + registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout); + registerBitsetWidgetLayout->addStretch(1); + + this->registerHistoryWidget->setFixedHeight(this->contentContainer->sizeHint().height()); + + QObject::connect(this->helpButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::openHelpPage); + QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close); QObject::connect( - bitsetWidget, - &BitsetWidget::byteChanged, + this->refreshValueButton, + &QPushButton::clicked, this, - &TargetRegisterInspectorWindow::updateRegisterValueInputField + &TargetRegisterInspectorWindow::refreshRegisterValue ); - this->bitsetWidgets.push_back(bitsetWidget); + QObject::connect(this->applyButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::applyChanges); - if (((registerByteIndex + 1) % 2) == 0) { - registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout); - bitsetSingleHorizontalLayout = new QHBoxLayout(); - bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING); - bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0); + QObject::connect( + this->registerHistoryWidget, + &RegisterHistoryWidget::historyItemSelected, + this, + &TargetRegisterInspectorWindow::onHistoryItemSelected + ); + + QObject::connect( + this->registerValueTextInput, + &QLineEdit::textEdited, + this, + &TargetRegisterInspectorWindow::onValueTextInputChanged + ); + + QObject::connect( + &insightWorker, + &InsightWorker::targetStateUpdated, + this, + &TargetRegisterInspectorWindow::onTargetStateChanged + ); + + this->updateRegisterValueInputField(); + this->onTargetStateChanged(currentTargetState); + + // Position the inspection window at the center of the main Insight window + this->move(parent->window()->geometry().center() - this->rect().center()); + + this->show(); + } + + bool TargetRegisterInspectorWindow::registerSupported(const Targets::TargetRegisterDescriptor& descriptor) { + return (descriptor.size > 0 && descriptor.size <= 8); + } + + void TargetRegisterInspectorWindow::setValue(const Targets::TargetMemoryBuffer& registerValue) { + this->registerValue = registerValue; + this->registerHistoryWidget->updateCurrentItemValue(this->registerValue); + this->registerHistoryWidget->selectCurrentItem(); + } + + void TargetRegisterInspectorWindow::onValueTextInputChanged(QString text) { + if (text.isEmpty()) { + text = "0"; } - byteNumber--; - } + bool validHexValue = false; + text.toLongLong(&validHexValue, 16); + if (!validHexValue) { + return; + } - registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout); - registerBitsetWidgetLayout->addStretch(1); + auto registerSize = this->registerDescriptor.size; + auto newValue = QByteArray::fromHex( + text.remove("0x", Qt::CaseInsensitive).toLatin1() + ).rightJustified(registerSize, 0).right(registerSize); - this->registerHistoryWidget->setFixedHeight(this->contentContainer->sizeHint().height()); + assert(newValue.size() >= registerSize); + assert(registerValue.size() == registerSize); + for (std::uint32_t byteIndex = 0; byteIndex < registerSize; byteIndex++) { + this->registerValue.at(byteIndex) = static_cast(newValue.at(byteIndex)); + } - QObject::connect(this->helpButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::openHelpPage); - QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close); - QObject::connect( - this->refreshValueButton, - &QPushButton::clicked, - this, - &TargetRegisterInspectorWindow::refreshRegisterValue - ); - QObject::connect(this->applyButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::applyChanges); - - QObject::connect( - this->registerHistoryWidget, - &RegisterHistoryWidget::historyItemSelected, - this, - &TargetRegisterInspectorWindow::onHistoryItemSelected - ); - - QObject::connect( - this->registerValueTextInput, - &QLineEdit::textEdited, - this, - &TargetRegisterInspectorWindow::onValueTextInputChanged - ); - - QObject::connect( - &insightWorker, - &InsightWorker::targetStateUpdated, - this, - &TargetRegisterInspectorWindow::onTargetStateChanged - ); - - this->updateRegisterValueInputField(); - this->onTargetStateChanged(currentTargetState); - - // Position the inspection window at the center of the main Insight window - this->move(parent->window()->geometry().center() - this->rect().center()); - - this->show(); -} - -bool TargetRegisterInspectorWindow::registerSupported(const Targets::TargetRegisterDescriptor& descriptor) { - return (descriptor.size > 0 && descriptor.size <= 8); -} - -void TargetRegisterInspectorWindow::setValue(const Targets::TargetMemoryBuffer& registerValue) { - this->registerValue = registerValue; - this->registerHistoryWidget->updateCurrentItemValue(this->registerValue); - this->registerHistoryWidget->selectCurrentItem(); -} - -void TargetRegisterInspectorWindow::onValueTextInputChanged(QString text) { - if (text.isEmpty()) { - text = "0"; - } - - bool validHexValue = false; - text.toLongLong(&validHexValue, 16); - if (!validHexValue) { - return; - } - - auto registerSize = this->registerDescriptor.size; - auto newValue = QByteArray::fromHex( - text.remove("0x", Qt::CaseInsensitive).toLatin1() - ).rightJustified(registerSize, 0).right(registerSize); - - assert(newValue.size() >= registerSize); - assert(registerValue.size() == registerSize); - for (std::uint32_t byteIndex = 0; byteIndex < registerSize; byteIndex++) { - this->registerValue.at(byteIndex) = static_cast(newValue.at(byteIndex)); - } - - for (auto& bitsetWidget : this->bitsetWidgets) { - bitsetWidget->updateValue(); - } -} - -void TargetRegisterInspectorWindow::onTargetStateChanged(TargetState newState) { - if (newState != TargetState::STOPPED) { - this->registerValueTextInput->setDisabled(true); - this->registerValueBitsetWidgetContainer->setDisabled(true); - this->applyButton->setDisabled(true); - this->refreshValueButton->setDisabled(true); - - } else if (this->targetState != TargetState::STOPPED && this->registerValueContainer->isEnabled()) { - this->registerValueBitsetWidgetContainer->setDisabled(false); - this->refreshValueButton->setDisabled(false); - - if (this->registerDescriptor.writable) { - this->registerValueTextInput->setDisabled(false); - this->applyButton->setDisabled(false); + for (auto& bitsetWidget : this->bitsetWidgets) { + bitsetWidget->updateValue(); } } - this->targetState = newState; -} + void TargetRegisterInspectorWindow::onTargetStateChanged(TargetState newState) { + if (newState != TargetState::STOPPED) { + this->registerValueTextInput->setDisabled(true); + this->registerValueBitsetWidgetContainer->setDisabled(true); + this->applyButton->setDisabled(true); + this->refreshValueButton->setDisabled(true); -void TargetRegisterInspectorWindow::onHistoryItemSelected(const Targets::TargetMemoryBuffer& selectedRegisterValue) { - this->registerValue = selectedRegisterValue; - this->updateValue(); + } else if (this->targetState != TargetState::STOPPED && this->registerValueContainer->isEnabled()) { + this->registerValueBitsetWidgetContainer->setDisabled(false); + this->refreshValueButton->setDisabled(false); - if (this->registerHistoryWidget->isCurrentItemSelected()) { - this->refreshValueButton->setVisible(true); - - } else { - this->refreshValueButton->setVisible(false); - } -} - -void TargetRegisterInspectorWindow::updateRegisterValueInputField() { - auto value = QByteArray( - reinterpret_cast(this->registerValue.data()), - static_cast(this->registerValue.size()) - ); - - this->registerValueTextInput->setText("0x" + QString(value.toHex()).toUpper()); -} - -void TargetRegisterInspectorWindow::updateRegisterValueBitsetWidgets() { - for (auto& bitsetWidget : this->bitsetWidgets) { - bitsetWidget->updateValue(); - } -} - -void TargetRegisterInspectorWindow::updateValue() { - this->updateRegisterValueInputField(); - this->updateRegisterValueBitsetWidgets(); -} - -void TargetRegisterInspectorWindow::refreshRegisterValue() { - this->registerValueContainer->setDisabled(true); - auto* readTargetRegisterTask = new ReadTargetRegisters({this->registerDescriptor}); - - QObject::connect( - readTargetRegisterTask, - &ReadTargetRegisters::targetRegistersRead, - this, - [this] (Targets::TargetRegisters targetRegisters) { - this->registerValueContainer->setDisabled(false); - - for (const auto& targetRegister : targetRegisters) { - if (targetRegister.descriptor == this->registerDescriptor) { - this->setValue(targetRegister.value); - } + if (this->registerDescriptor.writable) { + this->registerValueTextInput->setDisabled(false); + this->applyButton->setDisabled(false); } } - ); - QObject::connect( - readTargetRegisterTask, - &InsightWorkerTask::failed, - this, - [this] { - this->registerValueContainer->setDisabled(false); + this->targetState = newState; + } + + void TargetRegisterInspectorWindow::onHistoryItemSelected( + const Targets::TargetMemoryBuffer& selectedRegisterValue + ) { + this->registerValue = selectedRegisterValue; + this->updateValue(); + + if (this->registerHistoryWidget->isCurrentItemSelected()) { + this->refreshValueButton->setVisible(true); + + } else { + this->refreshValueButton->setVisible(false); } - ); + } - this->insightWorker.queueTask(readTargetRegisterTask); -} - -void TargetRegisterInspectorWindow::applyChanges() { - this->registerValueContainer->setDisabled(true); - const auto targetRegister = Targets::TargetRegister(this->registerDescriptor, this->registerValue); - auto* writeRegisterTask = new WriteTargetRegister(targetRegister); - - QObject::connect(writeRegisterTask, &InsightWorkerTask::completed, this, [this, targetRegister] { - this->registerValueContainer->setDisabled(false); - emit this->insightWorker.targetRegistersWritten( - {targetRegister}, - DateTime::currentDateTime() + void TargetRegisterInspectorWindow::updateRegisterValueInputField() { + auto value = QByteArray( + reinterpret_cast(this->registerValue.data()), + static_cast(this->registerValue.size()) ); - this->registerHistoryWidget->updateCurrentItemValue(targetRegister.value); - this->registerHistoryWidget->selectCurrentItem(); - }); - QObject::connect(writeRegisterTask, &InsightWorkerTask::failed, this, [this] (QString errorMessage) { - this->registerValueContainer->setDisabled(false); - auto* errorDialogue = new ErrorDialogue( - "Error", - "Failed to update " + QString::fromStdString( - this->registerDescriptor.name.value_or("") - ).toUpper() + " register value - " + errorMessage, - this + this->registerValueTextInput->setText("0x" + QString(value.toHex()).toUpper()); + } + + void TargetRegisterInspectorWindow::updateRegisterValueBitsetWidgets() { + for (auto& bitsetWidget : this->bitsetWidgets) { + bitsetWidget->updateValue(); + } + } + + void TargetRegisterInspectorWindow::updateValue() { + this->updateRegisterValueInputField(); + this->updateRegisterValueBitsetWidgets(); + } + + void TargetRegisterInspectorWindow::refreshRegisterValue() { + this->registerValueContainer->setDisabled(true); + auto* readTargetRegisterTask = new ReadTargetRegisters({this->registerDescriptor}); + + QObject::connect( + readTargetRegisterTask, + &ReadTargetRegisters::targetRegistersRead, + this, + [this] (Targets::TargetRegisters targetRegisters) { + this->registerValueContainer->setDisabled(false); + + for (const auto& targetRegister : targetRegisters) { + if (targetRegister.descriptor == this->registerDescriptor) { + this->setValue(targetRegister.value); + } + } + } ); - errorDialogue->show(); - }); - this->insightWorker.queueTask(writeRegisterTask); -} + QObject::connect( + readTargetRegisterTask, + &InsightWorkerTask::failed, + this, + [this] { + this->registerValueContainer->setDisabled(false); + } + ); -void TargetRegisterInspectorWindow::openHelpPage() { - QDesktopServices::openUrl(QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/register-inspection"))); + this->insightWorker.queueTask(readTargetRegisterTask); + } + + void TargetRegisterInspectorWindow::applyChanges() { + this->registerValueContainer->setDisabled(true); + const auto targetRegister = Targets::TargetRegister( + this->registerDescriptor, + this->registerValue + ); + auto* writeRegisterTask = new WriteTargetRegister(targetRegister); + + QObject::connect(writeRegisterTask, &InsightWorkerTask::completed, this, [this, targetRegister] { + this->registerValueContainer->setDisabled(false); + emit this->insightWorker.targetRegistersWritten( + {targetRegister}, + DateTime::currentDateTime() + ); + this->registerHistoryWidget->updateCurrentItemValue(targetRegister.value); + this->registerHistoryWidget->selectCurrentItem(); + }); + + QObject::connect(writeRegisterTask, &InsightWorkerTask::failed, this, [this] (QString errorMessage) { + this->registerValueContainer->setDisabled(false); + auto* errorDialogue = new ErrorDialogue( + "Error", + "Failed to update " + QString::fromStdString( + this->registerDescriptor.name.value_or("") + ).toUpper() + " register value - " + errorMessage, + this + ); + errorDialogue->show(); + }); + + this->insightWorker.queueTask(writeRegisterTask); + } + + void TargetRegisterInspectorWindow::openHelpPage() { + QDesktopServices::openUrl( + QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/register-inspection")) + ); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/TargetRegisterInspectorWindow.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/TargetRegisterInspectorWindow.hpp index 8cf48d9a..91f5b5db 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/TargetRegisterInspectorWindow.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/TargetRegisterInspectorWindow.hpp @@ -29,7 +29,7 @@ namespace Bloom::Widgets const Targets::TargetRegisterDescriptor& registerDescriptor, InsightWorker& insightWorker, Targets::TargetState currentTargetState, - std::optional registerValue = std::nullopt, + const std::optional& registerValue = std::nullopt, QWidget* parent = nullptr ); diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.cpp index b6e6e4aa..923444cb 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.cpp @@ -2,27 +2,28 @@ #include -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + ItemWidget::ItemWidget(QWidget* parent): ClickableWidget(parent) { + auto onClick = [this] { + this->setSelected(true); + }; -ItemWidget::ItemWidget(QWidget* parent): ClickableWidget(parent) { - auto onClick = [this] { - this->setSelected(true); - }; + QObject::connect(this, &ClickableWidget::clicked, this, onClick); + QObject::connect(this, &ClickableWidget::rightClicked, this, onClick); - QObject::connect(this, &ClickableWidget::clicked, this, onClick); - QObject::connect(this, &ClickableWidget::rightClicked, this, onClick); - - this->setSelected(false); -} - -void ItemWidget::setSelected(bool selected) { - this->setProperty("selected", selected); - this->style()->unpolish(this); - this->style()->polish(this); - - if (selected) { - emit this->selected(this); + this->setSelected(false); } - this->postSetSelected(selected); + void ItemWidget::setSelected(bool selected) { + this->setProperty("selected", selected); + this->style()->unpolish(this); + this->style()->polish(this); + + if (selected) { + emit this->selected(this); + } + + this->postSetSelected(selected); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.cpp index e4ea19ab..07f8decf 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.cpp @@ -8,143 +8,145 @@ #include "src/Helpers/Paths.hpp" -using namespace Bloom::Widgets; -using namespace Bloom::Exceptions; +namespace Bloom::Widgets +{ + using namespace Bloom::Exceptions; -using Bloom::Targets::TargetRegisterDescriptor; + using Bloom::Targets::TargetRegisterDescriptor; -RegisterGroupWidget::RegisterGroupWidget( - QString name, - const std::set& registerDescriptors, - InsightWorker& insightWorker, - TargetRegistersPaneWidget* parent -): ItemWidget(parent), name(std::move(name)) { - this->setObjectName(this->name); + RegisterGroupWidget::RegisterGroupWidget( + QString name, + const std::set& registerDescriptors, + InsightWorker& insightWorker, + TargetRegistersPaneWidget* parent + ): ItemWidget(parent), name(std::move(name)) { + this->setObjectName(this->name); - this->headerWidget->setObjectName("register-group-header"); - this->headerWidget->setFixedHeight(25); - auto* headerLayout = new QHBoxLayout(this->headerWidget); - headerLayout->setContentsMargins(5, 0, 0, 0); + this->headerWidget->setObjectName("register-group-header"); + this->headerWidget->setFixedHeight(25); + auto* headerLayout = new QHBoxLayout(this->headerWidget); + headerLayout->setContentsMargins(5, 0, 0, 0); - this->label->setText(this->name); + this->label->setText(this->name); - this->arrowIcon->setObjectName("arrow-icon"); - auto static arrowIconPath = QString::fromStdString( - Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg" - ); - this->arrowIcon->setSvgFilePath(arrowIconPath); - this->arrowIcon->setContainerHeight(15); - this->arrowIcon->setContainerWidth(14); + this->arrowIcon->setObjectName("arrow-icon"); + auto static arrowIconPath = QString::fromStdString( + Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg" + ); + this->arrowIcon->setSvgFilePath(arrowIconPath); + this->arrowIcon->setContainerHeight(15); + this->arrowIcon->setContainerWidth(14); - this->registerGroupIcon->setObjectName("register-group-icon"); - auto static registerIconPath = QString::fromStdString( - Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg" - ); - this->registerGroupIcon->setSvgFilePath(registerIconPath); - this->registerGroupIcon->setContainerHeight(15); - this->registerGroupIcon->setContainerWidth(15); + this->registerGroupIcon->setObjectName("register-group-icon"); + auto static registerIconPath = QString::fromStdString( + Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg" + ); + this->registerGroupIcon->setSvgFilePath(registerIconPath); + this->registerGroupIcon->setContainerHeight(15); + this->registerGroupIcon->setContainerWidth(15); - headerLayout->addWidget(this->arrowIcon); - headerLayout->addWidget(this->registerGroupIcon); - headerLayout->addWidget(this->label); + headerLayout->addWidget(this->arrowIcon); + headerLayout->addWidget(this->registerGroupIcon); + headerLayout->addWidget(this->label); - auto* bodyLayout = new QVBoxLayout(this->bodyWidget); - bodyLayout->setContentsMargins(0, 0,0,0); - bodyLayout->setSpacing(0); + auto* bodyLayout = new QVBoxLayout(this->bodyWidget); + bodyLayout->setContentsMargins(0, 0,0,0); + bodyLayout->setSpacing(0); - for (const auto& descriptor : registerDescriptors) { - if (!descriptor.name.has_value()) { - continue; + for (const auto& descriptor : registerDescriptors) { + if (!descriptor.name.has_value()) { + continue; + } + + if (!descriptor.readable) { + continue; + } + + auto* registerWidget = new RegisterWidget(descriptor, insightWorker, this->bodyWidget); + bodyLayout->addWidget(registerWidget, 0, Qt::AlignmentFlag::AlignTop); + + QObject::connect( + registerWidget, + &ItemWidget::selected, + parent, + &TargetRegistersPaneWidget::onItemSelectionChange + ); + + this->registerWidgets.insert(registerWidget); + this->registerWidgetsMappedByDescriptor.insert(std::pair(descriptor, registerWidget)); } - if (!descriptor.readable) { - continue; - } + bodyLayout->addStretch(1); - auto* registerWidget = new RegisterWidget(descriptor, insightWorker, this->bodyWidget); - bodyLayout->addWidget(registerWidget, 0, Qt::AlignmentFlag::AlignTop); + this->layout->setContentsMargins(0,0,0,0); + this->layout->setSpacing(0); + this->layout->addWidget(this->headerWidget, 0, Qt::AlignmentFlag::AlignTop); + this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignTop); + this->layout->addStretch(1); + + this->collapse(); + + QObject::connect(this->headerWidget, &ClickableWidget::doubleClicked, [this] { + if (this->collapsed) { + this->expand(); + + } else { + this->collapse(); + } + }); QObject::connect( - registerWidget, + this->headerWidget, &ItemWidget::selected, parent, &TargetRegistersPaneWidget::onItemSelectionChange ); - - this->registerWidgets.insert(registerWidget); - this->registerWidgetsMappedByDescriptor.insert(std::pair(descriptor, registerWidget)); } - bodyLayout->addStretch(1); + void RegisterGroupWidget::collapse() { + this->arrowIcon->setAngle(0); + this->bodyWidget->setVisible(false); + this->collapsed = true; + } - this->layout->setContentsMargins(0,0,0,0); - this->layout->setSpacing(0); - this->layout->addWidget(this->headerWidget, 0, Qt::AlignmentFlag::AlignTop); - this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignTop); - this->layout->addStretch(1); + void RegisterGroupWidget::expand() { + this->arrowIcon->setAngle(90); + this->bodyWidget->setVisible(true); + this->collapsed = false; + } - this->collapse(); + void RegisterGroupWidget::setAllRegistersVisible(bool visible) { + for (const auto& registerWidget : this->registerWidgets) { + registerWidget->setVisible(visible); + } + } - QObject::connect(this->headerWidget, &ClickableWidget::doubleClicked, [this] { - if (this->collapsed) { - this->expand(); + void RegisterGroupWidget::filterRegisters(const QString& keyword) { + int matchingWidgetCount = 0; + for (const auto& registerWidget : this->registerWidgets) { + if (keyword.isEmpty() || (registerWidget->searchKeywords.contains(keyword, Qt::CaseInsensitive))) { + matchingWidgetCount++; + registerWidget->setVisible(true); - } else { + } else { + registerWidget->setVisible(false); + } + } + + if (matchingWidgetCount == 0) { this->collapse(); - } - }); - - QObject::connect( - this->headerWidget, - &ItemWidget::selected, - parent, - &TargetRegistersPaneWidget::onItemSelectionChange - ); -} - -void RegisterGroupWidget::collapse() { - this->arrowIcon->setAngle(0); - this->bodyWidget->setVisible(false); - this->collapsed = true; -} - -void RegisterGroupWidget::expand() { - this->arrowIcon->setAngle(90); - this->bodyWidget->setVisible(true); - this->collapsed = false; -} - -void RegisterGroupWidget::setAllRegistersVisible(bool visible) { - for (const auto& registerWidget : this->registerWidgets) { - registerWidget->setVisible(visible); - } -} - -void RegisterGroupWidget::filterRegisters(const QString& keyword) { - int matchingWidgetCount = 0; - for (const auto& registerWidget : this->registerWidgets) { - if (keyword.isEmpty() || (registerWidget->searchKeywords.contains(keyword, Qt::CaseInsensitive))) { - matchingWidgetCount++; - registerWidget->setVisible(true); + this->setVisible(false); } else { - registerWidget->setVisible(false); - } - } + this->setVisible(true); + if (!keyword.isEmpty()) { + this->expand(); - if (matchingWidgetCount == 0) { - this->collapse(); - this->setVisible(false); - - } else { - this->setVisible(true); - if (!keyword.isEmpty()) { - this->expand(); - - } else { - this->collapse(); + } else { + this->collapse(); + } } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.cpp index 225c64ba..65449b72 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.cpp @@ -9,226 +9,229 @@ #include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp" -using namespace Bloom::Widgets; -using namespace Bloom::Exceptions; - -using Bloom::Targets::TargetRegisterDescriptor; - -RegisterWidget::RegisterWidget( - TargetRegisterDescriptor descriptor, - InsightWorker& insightWorker, - QWidget *parent -) - : ItemWidget(parent) - , descriptor(std::move(descriptor)) - , searchKeywords(QString::fromStdString( - this->descriptor.name.value_or("") + this->descriptor.description.value_or("") - ).toLower()) - , insightWorker(insightWorker) +namespace Bloom::Widgets { - this->setObjectName("register-item"); - this->setFixedHeight(25); + using namespace Bloom::Exceptions; - this->nameLabel->setText(QString::fromStdString(this->descriptor.name.value()).toUpper()); - this->valueLabel->setObjectName("value"); + using Bloom::Targets::TargetRegisterDescriptor; - this->registerIcon->setObjectName("register-icon"); - auto static registerIconPath = QString::fromStdString( - Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg" - ); - this->registerIcon->setSvgFilePath(registerIconPath); - this->registerIcon->setContainerHeight(15); - this->registerIcon->setContainerWidth(15); + RegisterWidget::RegisterWidget( + TargetRegisterDescriptor descriptor, + InsightWorker& insightWorker, + QWidget *parent + ) + : ItemWidget(parent) + , descriptor(std::move(descriptor)) + , searchKeywords(QString::fromStdString( + this->descriptor.name.value_or("") + this->descriptor.description.value_or("") + ).toLower()) + , insightWorker(insightWorker) + { + this->setObjectName("register-item"); + this->setFixedHeight(25); - this->layout->setContentsMargins(47, 0, 0, 0); - this->layout->setSpacing(0); - this->layout->addWidget(this->registerIcon); - this->layout->addSpacing(7); - this->layout->addWidget(this->nameLabel); - this->layout->addSpacing(5); - this->layout->addWidget(this->valueLabel); - this->layout->addStretch(1); + this->nameLabel->setText(QString::fromStdString(this->descriptor.name.value()).toUpper()); + this->valueLabel->setObjectName("value"); - QObject::connect(this, &ClickableWidget::doubleClicked, this, &RegisterWidget::openInspectionWindow); - QObject::connect(this->openInspectionWindowAction, &QAction::triggered, this, &RegisterWidget::openInspectionWindow); - QObject::connect(this->refreshValueAction, &QAction::triggered, this, &RegisterWidget::refreshValue); - QObject::connect(this->copyValueNameAction, &QAction::triggered, this, &RegisterWidget::copyName); - QObject::connect(this->copyValueHexAction, &QAction::triggered, this, &RegisterWidget::copyValueHex); - QObject::connect(this->copyValueDecimalAction, &QAction::triggered, this, &RegisterWidget::copyValueDecimal); - QObject::connect(this->copyValueBinaryAction, &QAction::triggered, this, &RegisterWidget::copyValueBinary); + this->registerIcon->setObjectName("register-icon"); + auto static registerIconPath = QString::fromStdString( + Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg" + ); + this->registerIcon->setSvgFilePath(registerIconPath); + this->registerIcon->setContainerHeight(15); + this->registerIcon->setContainerWidth(15); - QObject::connect( - &(this->insightWorker), - &InsightWorker::targetStateUpdated, - this, - &RegisterWidget::onTargetStateChange - ); -} + this->layout->setContentsMargins(47, 0, 0, 0); + this->layout->setSpacing(0); + this->layout->addWidget(this->registerIcon); + this->layout->addSpacing(7); + this->layout->addWidget(this->nameLabel); + this->layout->addSpacing(5); + this->layout->addWidget(this->valueLabel); + this->layout->addStretch(1); -void RegisterWidget::setRegisterValue(const Targets::TargetRegister& targetRegister) { - const auto valueChanged = this->currentRegister.has_value() - && this->currentRegister.value().value != targetRegister.value; - this->currentRegister = targetRegister; + QObject::connect(this, &ClickableWidget::doubleClicked, this, &RegisterWidget::openInspectionWindow); + QObject::connect(this->openInspectionWindowAction, &QAction::triggered, this, &RegisterWidget::openInspectionWindow); + QObject::connect(this->refreshValueAction, &QAction::triggered, this, &RegisterWidget::refreshValue); + QObject::connect(this->copyValueNameAction, &QAction::triggered, this, &RegisterWidget::copyName); + QObject::connect(this->copyValueHexAction, &QAction::triggered, this, &RegisterWidget::copyValueHex); + QObject::connect(this->copyValueDecimalAction, &QAction::triggered, this, &RegisterWidget::copyValueDecimal); + QObject::connect(this->copyValueBinaryAction, &QAction::triggered, this, &RegisterWidget::copyValueBinary); - auto valueByteArray = QByteArray( - reinterpret_cast(targetRegister.value.data()), - static_cast(targetRegister.value.size()) - ); - - auto hexValueByteArray = valueByteArray.toHex(); - auto registerValue = ": 0x" + QString(hexValueByteArray).toUpper() - + " | " + QString::number(hexValueByteArray.toUInt(nullptr, 16)); - - if (targetRegister.value.size() == 1 && targetRegister.value[0] >= 32 && targetRegister.value[0] <= 126) { - registerValue += " | '" + QString(valueByteArray) + "'"; + QObject::connect( + &(this->insightWorker), + &InsightWorker::targetStateUpdated, + this, + &RegisterWidget::onTargetStateChange + ); } - this->valueLabel->setProperty("changed", valueChanged); - this->valueLabel->style()->unpolish(this->valueLabel); - this->valueLabel->style()->polish(this->valueLabel); + void RegisterWidget::setRegisterValue(const Targets::TargetRegister& targetRegister) { + const auto valueChanged = this->currentRegister.has_value() + && this->currentRegister.value().value != targetRegister.value; + this->currentRegister = targetRegister; - this->valueLabel->setText(registerValue); - - if (this->inspectWindow != nullptr) { - this->inspectWindow->setValue(targetRegister.value); - } -} - -void RegisterWidget::clearInlineValue() { - this->valueLabel->clear(); -} - -void RegisterWidget::contextMenuEvent(QContextMenuEvent* event) { - this->setSelected(true); - - auto* menu = new QMenu(this); - menu->addAction(this->openInspectionWindowAction); - menu->addAction(this->refreshValueAction); - menu->addSeparator(); - - auto* copyMenu = new QMenu("Copy", this); - copyMenu->addAction(this->copyValueNameAction); - copyMenu->addSeparator(); - copyMenu->addAction(this->copyValueDecimalAction); - copyMenu->addAction(this->copyValueHexAction); - copyMenu->addAction(this->copyValueBinaryAction); - - menu->addMenu(copyMenu); - - this->openInspectionWindowAction->setEnabled(TargetRegisterInspectorWindow::registerSupported(this->descriptor)); - - const auto targetStopped = this->targetState == Targets::TargetState::STOPPED; - const auto targetStoppedAndValuePresent = targetStopped && this->currentRegister.has_value(); - this->refreshValueAction->setEnabled(targetStopped); - this->copyValueDecimalAction->setEnabled(targetStoppedAndValuePresent); - this->copyValueHexAction->setEnabled(targetStoppedAndValuePresent); - this->copyValueBinaryAction->setEnabled(targetStoppedAndValuePresent); - - menu->exec(event->globalPos()); -} - -void RegisterWidget::openInspectionWindow() { - if (!TargetRegisterInspectorWindow::registerSupported(this->descriptor)) { - return; - } - - if (this->inspectWindow == nullptr) { - this->inspectWindow = new TargetRegisterInspectorWindow( - this->descriptor, - this->insightWorker, - this->targetState, - this->currentRegister.has_value() ? std::optional(this->currentRegister->value) : std::nullopt, - this + auto valueByteArray = QByteArray( + reinterpret_cast(targetRegister.value.data()), + static_cast(targetRegister.value.size()) ); - } else { - if (this->currentRegister.has_value()) { - this->inspectWindow->setValue(this->currentRegister->value); + auto hexValueByteArray = valueByteArray.toHex(); + auto registerValue = ": 0x" + QString(hexValueByteArray).toUpper() + + " | " + QString::number(hexValueByteArray.toUInt(nullptr, 16)); + + if (targetRegister.value.size() == 1 && targetRegister.value[0] >= 32 && targetRegister.value[0] <= 126) { + registerValue += " | '" + QString(valueByteArray) + "'"; } - if (!this->inspectWindow->isVisible()) { - this->inspectWindow->show(); + this->valueLabel->setProperty("changed", valueChanged); + this->valueLabel->style()->unpolish(this->valueLabel); + this->valueLabel->style()->polish(this->valueLabel); + + this->valueLabel->setText(registerValue); + + if (this->inspectWindow != nullptr) { + this->inspectWindow->setValue(targetRegister.value); + } + } + + void RegisterWidget::clearInlineValue() { + this->valueLabel->clear(); + } + + void RegisterWidget::contextMenuEvent(QContextMenuEvent* event) { + this->setSelected(true); + + auto* menu = new QMenu(this); + menu->addAction(this->openInspectionWindowAction); + menu->addAction(this->refreshValueAction); + menu->addSeparator(); + + auto* copyMenu = new QMenu("Copy", this); + copyMenu->addAction(this->copyValueNameAction); + copyMenu->addSeparator(); + copyMenu->addAction(this->copyValueDecimalAction); + copyMenu->addAction(this->copyValueHexAction); + copyMenu->addAction(this->copyValueBinaryAction); + + menu->addMenu(copyMenu); + + this->openInspectionWindowAction->setEnabled(TargetRegisterInspectorWindow::registerSupported(this->descriptor)); + + const auto targetStopped = this->targetState == Targets::TargetState::STOPPED; + const auto targetStoppedAndValuePresent = targetStopped && this->currentRegister.has_value(); + this->refreshValueAction->setEnabled(targetStopped); + this->copyValueDecimalAction->setEnabled(targetStoppedAndValuePresent); + this->copyValueHexAction->setEnabled(targetStoppedAndValuePresent); + this->copyValueBinaryAction->setEnabled(targetStoppedAndValuePresent); + + menu->exec(event->globalPos()); + } + + void RegisterWidget::openInspectionWindow() { + if (!TargetRegisterInspectorWindow::registerSupported(this->descriptor)) { + return; + } + + if (this->inspectWindow == nullptr) { + this->inspectWindow = new TargetRegisterInspectorWindow( + this->descriptor, + this->insightWorker, + this->targetState, + this->currentRegister.has_value() ? std::optional(this->currentRegister->value) + : std::nullopt, + this + ); } else { - this->inspectWindow->activateWindow(); - } - } -} + if (this->currentRegister.has_value()) { + this->inspectWindow->setValue(this->currentRegister->value); + } -void RegisterWidget::refreshValue() { - auto* readRegisterTask = new ReadTargetRegisters({this->descriptor}); + if (!this->inspectWindow->isVisible()) { + this->inspectWindow->show(); - QObject::connect( - readRegisterTask, - &ReadTargetRegisters::targetRegistersRead, - this, - [this] (Targets::TargetRegisters registers) { - for (const auto& targetRegister : registers) { - if (targetRegister.descriptor == this->descriptor) { - this->setRegisterValue(targetRegister); - } + } else { + this->inspectWindow->activateWindow(); } } - ); - - this->insightWorker.queueTask(readRegisterTask); -} - -void RegisterWidget::copyName() { - if (this->nameLabel != nullptr) { - QApplication::clipboard()->setText(this->nameLabel->text()); } -} -void RegisterWidget::copyValueHex() { - if (this->currentRegister.has_value()) { - auto valueByteArray = QByteArray( - reinterpret_cast(this->currentRegister.value().value.data()), - static_cast(this->currentRegister.value().value.size()) - ).toHex(); - QApplication::clipboard()->setText(QString(valueByteArray).toUpper()); + void RegisterWidget::refreshValue() { + auto* readRegisterTask = new ReadTargetRegisters({this->descriptor}); + + QObject::connect( + readRegisterTask, + &ReadTargetRegisters::targetRegistersRead, + this, + [this] (Targets::TargetRegisters registers) { + for (const auto& targetRegister : registers) { + if (targetRegister.descriptor == this->descriptor) { + this->setRegisterValue(targetRegister); + } + } + } + ); + + this->insightWorker.queueTask(readRegisterTask); } -} -void RegisterWidget::copyValueDecimal() { - if (this->currentRegister.has_value()) { - auto valueByteArray = QByteArray( - reinterpret_cast(this->currentRegister.value().value.data()), - static_cast(this->currentRegister.value().value.size()) - ).toHex(); - QApplication::clipboard()->setText(QString::number(valueByteArray.toUInt(nullptr, 16))); - } -} - -void RegisterWidget::copyValueBinary() { - if (this->currentRegister.has_value()) { - const auto registerValueSize = static_cast(this->currentRegister.value().size()); - auto valueByteArray = QByteArray( - reinterpret_cast(this->currentRegister.value().value.data()), - registerValueSize - ).toHex(); - - auto bitString = QString::number(valueByteArray.toUInt(nullptr, 16), 2); - - if (bitString.size() < (registerValueSize * 8)) { - bitString = bitString.rightJustified((registerValueSize * 8), '0'); + void RegisterWidget::copyName() { + if (this->nameLabel != nullptr) { + QApplication::clipboard()->setText(this->nameLabel->text()); } + } - QApplication::clipboard()->setText(bitString); - } -} - -void RegisterWidget::postSetSelected(bool selected) { - auto valueLabelStyle = this->valueLabel->style(); - valueLabelStyle->unpolish(this->valueLabel); - valueLabelStyle->polish(this->valueLabel); -} - -void RegisterWidget::onTargetStateChange(Targets::TargetState newState) { - this->targetState = newState; - - if (this->targetState == Targets::TargetState::RUNNING) { - this->clearInlineValue(); + void RegisterWidget::copyValueHex() { + if (this->currentRegister.has_value()) { + auto valueByteArray = QByteArray( + reinterpret_cast(this->currentRegister.value().value.data()), + static_cast(this->currentRegister.value().value.size()) + ).toHex(); + QApplication::clipboard()->setText(QString(valueByteArray).toUpper()); + } + } + + void RegisterWidget::copyValueDecimal() { + if (this->currentRegister.has_value()) { + auto valueByteArray = QByteArray( + reinterpret_cast(this->currentRegister.value().value.data()), + static_cast(this->currentRegister.value().value.size()) + ).toHex(); + QApplication::clipboard()->setText(QString::number(valueByteArray.toUInt(nullptr, 16))); + } + } + + void RegisterWidget::copyValueBinary() { + if (this->currentRegister.has_value()) { + const auto registerValueSize = static_cast(this->currentRegister.value().size()); + auto valueByteArray = QByteArray( + reinterpret_cast(this->currentRegister.value().value.data()), + registerValueSize + ).toHex(); + + auto bitString = QString::number(valueByteArray.toUInt(nullptr, 16), 2); + + if (bitString.size() < (registerValueSize * 8)) { + bitString = bitString.rightJustified((registerValueSize * 8), '0'); + } + + QApplication::clipboard()->setText(bitString); + } + } + + void RegisterWidget::postSetSelected(bool selected) { + auto valueLabelStyle = this->valueLabel->style(); + valueLabelStyle->unpolish(this->valueLabel); + valueLabelStyle->polish(this->valueLabel); + } + + void RegisterWidget::onTargetStateChange(Targets::TargetState newState) { + this->targetState = newState; + + if (this->targetState == Targets::TargetState::RUNNING) { + this->clearInlineValue(); + } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.cpp index 979bcc92..110d04b7 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.cpp @@ -12,239 +12,244 @@ #include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp" -using namespace Bloom::Widgets; -using namespace Bloom::Exceptions; +namespace Bloom::Widgets +{ + using namespace Bloom::Exceptions; -using Bloom::Targets::TargetDescriptor; -using Bloom::Targets::TargetRegisterDescriptor; -using Bloom::Targets::TargetRegisterDescriptors; -using Bloom::Targets::TargetRegisterType; + using Bloom::Targets::TargetDescriptor; + using Bloom::Targets::TargetRegisterDescriptor; + using Bloom::Targets::TargetRegisterDescriptors; + using Bloom::Targets::TargetRegisterType; -TargetRegistersPaneWidget::TargetRegistersPaneWidget( - const TargetDescriptor& targetDescriptor, - InsightWorker& insightWorker, - PanelWidget* parent -): QWidget(parent), parent(parent), targetDescriptor(targetDescriptor), insightWorker(insightWorker) { - this->setObjectName("target-registers-side-pane"); + TargetRegistersPaneWidget::TargetRegistersPaneWidget( + const TargetDescriptor& targetDescriptor, + InsightWorker& insightWorker, + PanelWidget* parent + ): QWidget(parent), parent(parent), targetDescriptor(targetDescriptor), insightWorker(insightWorker) { + this->setObjectName("target-registers-side-pane"); - auto targetRegistersPaneUiFile = QFile( - QString::fromStdString(Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/TargetRegistersSidePane.ui" - ) - ); + auto targetRegistersPaneUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/" + "TargetRegistersSidePane.ui" + ) + ); - if (!targetRegistersPaneUiFile.open(QFile::ReadOnly)) { - throw Exception("Failed to open TargetRegistersSidePane UI file"); - } + if (!targetRegistersPaneUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open TargetRegistersSidePane UI file"); + } - auto uiLoader = UiLoader(this); - this->container = uiLoader.load(&targetRegistersPaneUiFile, this); - this->container->setFixedSize(parent->width(), parent->maximumHeight()); + auto uiLoader = UiLoader(this); + this->container = uiLoader.load(&targetRegistersPaneUiFile, this); + this->container->setFixedSize(parent->width(), parent->maximumHeight()); - auto* layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(this->container); + auto* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(this->container); - this->toolBar = this->container->findChild("tool-bar"); - this->collapseAllButton = this->toolBar->findChild("collapse-all-btn"); - this->expandAllButton = this->toolBar->findChild("expand-all-btn"); - this->toolBar->layout()->setContentsMargins(5, 0, 5, 0); - this->searchInput = this->container->findChild("search-input"); + this->toolBar = this->container->findChild("tool-bar"); + this->collapseAllButton = this->toolBar->findChild("collapse-all-btn"); + this->expandAllButton = this->toolBar->findChild("expand-all-btn"); + this->toolBar->layout()->setContentsMargins(5, 0, 5, 0); + this->searchInput = this->container->findChild("search-input"); - QObject::connect(this->expandAllButton, &QToolButton::clicked, [this] { - this->expandAllRegisterGroups(); - }); + QObject::connect(this->expandAllButton, &QToolButton::clicked, [this] { + this->expandAllRegisterGroups(); + }); - QObject::connect(this->collapseAllButton, &QToolButton::clicked, [this] { - this->collapseAllRegisterGroups(); - }); + QObject::connect(this->collapseAllButton, &QToolButton::clicked, [this] { + this->collapseAllRegisterGroups(); + }); - QObject::connect(this->searchInput, &QLineEdit::textChanged, [this] { - this->filterRegisters(this->searchInput->text()); - }); + QObject::connect(this->searchInput, &QLineEdit::textChanged, [this] { + this->filterRegisters(this->searchInput->text()); + }); - this->itemScrollArea = this->container->findChild("item-scroll-area"); + this->itemScrollArea = this->container->findChild("item-scroll-area"); - this->itemContainer = this->container->findChild("item-container"); - auto itemLayout = this->itemContainer->findChild(); + this->itemContainer = this->container->findChild("item-container"); + auto itemLayout = this->itemContainer->findChild(); - auto& registerDescriptors = targetDescriptor.registerDescriptorsByType; - this->renderedDescriptors = std::set( - registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).begin(), - registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).end() - ); + auto& registerDescriptors = targetDescriptor.registerDescriptorsByType; + this->renderedDescriptors = std::set( + registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).begin(), + registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).end() + ); - auto* generalPurposeRegisterGroupWidget = new RegisterGroupWidget( - "CPU General Purpose", - this->renderedDescriptors, - insightWorker, - this - ); - - itemLayout->addWidget(generalPurposeRegisterGroupWidget, 0, Qt::AlignTop); - this->registerGroupWidgets.insert(generalPurposeRegisterGroupWidget); - - auto registerDescriptorsByGroupName = std::map>(); - for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::OTHER)) { - registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor); - } - - for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::PORT_REGISTER)) { - registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor); - } - - for (const auto& [groupName, registerDescriptors] : registerDescriptorsByGroupName) { - auto* registerGroupWidget = new RegisterGroupWidget( - QString::fromStdString(groupName).toUpper(), - registerDescriptors, + auto* generalPurposeRegisterGroupWidget = new RegisterGroupWidget( + "CPU General Purpose", + this->renderedDescriptors, insightWorker, this ); - itemLayout->addWidget(registerGroupWidget, 0, Qt::AlignTop); - this->registerGroupWidgets.insert(registerGroupWidget); - this->renderedDescriptors.insert(registerDescriptors.begin(), registerDescriptors.end()); - } + itemLayout->addWidget(generalPurposeRegisterGroupWidget, 0, Qt::AlignTop); + this->registerGroupWidgets.insert(generalPurposeRegisterGroupWidget); - itemLayout->addStretch(1); - - QObject::connect( - &insightWorker, - &InsightWorker::targetStateUpdated, - this, - &TargetRegistersPaneWidget::onTargetStateChanged - ); - - QObject::connect( - &insightWorker, - &InsightWorker::targetRegistersWritten, - this, - &TargetRegistersPaneWidget::onRegistersRead - ); -} - -void TargetRegistersPaneWidget::filterRegisters(const QString& keyword) { - for (const auto& registerGroupWidget : this->registerGroupWidgets) { - // If the group name matches the keyword, then don't bother iterating through all the register widgets - if (keyword.isEmpty() || registerGroupWidget->name.contains(keyword, Qt::CaseInsensitive)) { - registerGroupWidget->setVisible(true); - registerGroupWidget->setAllRegistersVisible(true); - - if (!keyword.isEmpty()) { - registerGroupWidget->expand(); - - } else { - registerGroupWidget->collapse(); - } - - } else { - registerGroupWidget->filterRegisters(keyword); + auto registerDescriptorsByGroupName = std::map>(); + for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::OTHER)) { + registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor); } - } -} -void TargetRegistersPaneWidget::collapseAllRegisterGroups() { - for (const auto& registerGroupWidget : this->registerGroupWidgets) { - registerGroupWidget->collapse(); - } -} + for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::PORT_REGISTER)) { + registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor); + } -void TargetRegistersPaneWidget::expandAllRegisterGroups() { - for (const auto& registerGroupWidget : this->registerGroupWidgets) { - registerGroupWidget->expand(); - } -} + for (const auto& [groupName, registerDescriptors] : registerDescriptorsByGroupName) { + auto* registerGroupWidget = new RegisterGroupWidget( + QString::fromStdString(groupName).toUpper(), + registerDescriptors, + insightWorker, + this + ); -void TargetRegistersPaneWidget::refreshRegisterValues(std::optional> callback) { - auto& descriptors = this->renderedDescriptors; + itemLayout->addWidget(registerGroupWidget, 0, Qt::AlignTop); + this->registerGroupWidgets.insert(registerGroupWidget); + this->renderedDescriptors.insert(registerDescriptors.begin(), registerDescriptors.end()); + } - if (descriptors.empty()) { - return; - } + itemLayout->addStretch(1); - auto* readRegisterTask = new ReadTargetRegisters(descriptors); - QObject::connect( - readRegisterTask, - &ReadTargetRegisters::targetRegistersRead, - this, - &TargetRegistersPaneWidget::onRegistersRead - ); - - if (callback.has_value()) { QObject::connect( - readRegisterTask, - &InsightWorkerTask::completed, + &insightWorker, + &InsightWorker::targetStateUpdated, this, - callback.value() + &TargetRegistersPaneWidget::onTargetStateChanged + ); + + QObject::connect( + &insightWorker, + &InsightWorker::targetRegistersWritten, + this, + &TargetRegistersPaneWidget::onRegistersRead ); } - this->insightWorker.queueTask(readRegisterTask); -} + void TargetRegistersPaneWidget::filterRegisters(const QString& keyword) { + for (const auto& registerGroupWidget : this->registerGroupWidgets) { + // If the group name matches the keyword, then don't bother iterating through all the register widgets + if (keyword.isEmpty() || registerGroupWidget->name.contains(keyword, Qt::CaseInsensitive)) { + registerGroupWidget->setVisible(true); + registerGroupWidget->setAllRegistersVisible(true); -void TargetRegistersPaneWidget::activate() { - this->show(); - this->activated = true; - this->postActivate(); -} + if (!keyword.isEmpty()) { + registerGroupWidget->expand(); -void TargetRegistersPaneWidget::deactivate() { - this->hide(); - this->activated = false; - this->postDeactivate(); -} + } else { + registerGroupWidget->collapse(); + } -void TargetRegistersPaneWidget::onItemSelectionChange(ItemWidget* newlySelectedWidget) { - // Only one item in the target registers pane can be selected at any given time. - if (this->selectedItemWidget != newlySelectedWidget) { - if (this->selectedItemWidget != nullptr) { - this->selectedItemWidget->setSelected(false); + } else { + registerGroupWidget->filterRegisters(keyword); + } + } + } + + void TargetRegistersPaneWidget::collapseAllRegisterGroups() { + for (const auto& registerGroupWidget : this->registerGroupWidgets) { + registerGroupWidget->collapse(); + } + } + + void TargetRegistersPaneWidget::expandAllRegisterGroups() { + for (const auto& registerGroupWidget : this->registerGroupWidgets) { + registerGroupWidget->expand(); + } + } + + void TargetRegistersPaneWidget::refreshRegisterValues(std::optional> callback) { + auto& descriptors = this->renderedDescriptors; + + if (descriptors.empty()) { + return; } - this->selectedItemWidget = newlySelectedWidget; + auto* readRegisterTask = new ReadTargetRegisters(descriptors); + QObject::connect( + readRegisterTask, + &ReadTargetRegisters::targetRegistersRead, + this, + &TargetRegistersPaneWidget::onRegistersRead + ); + + if (callback.has_value()) { + QObject::connect( + readRegisterTask, + &InsightWorkerTask::completed, + this, + callback.value() + ); + } + + this->insightWorker.queueTask(readRegisterTask); } -} -void TargetRegistersPaneWidget::resizeEvent(QResizeEvent* event) { - const auto parentSize = this->parent->size(); - const auto width = parentSize.width() - 1; - this->container->setFixedWidth(width); - this->searchInput->setFixedWidth(width - 20); - - /* - * In order to avoid the panel resize handle overlapping the scroll bar handle, we reduce the size of - * the scroll area. - */ - this->itemScrollArea->setFixedWidth(width - this->parent->getHandleSize()); -} - -void TargetRegistersPaneWidget::postActivate() { - if (this->targetState == Targets::TargetState::STOPPED) { - this->refreshRegisterValues(); + void TargetRegistersPaneWidget::activate() { + this->show(); + this->activated = true; + this->postActivate(); } -} -void TargetRegistersPaneWidget::postDeactivate() { - -} - -void TargetRegistersPaneWidget::onTargetStateChanged(Targets::TargetState newState) { - using Targets::TargetState; - this->targetState = newState; - - if (newState == TargetState::STOPPED && this->activated) { - this->refreshRegisterValues(); + void TargetRegistersPaneWidget::deactivate() { + this->hide(); + this->activated = false; + this->postDeactivate(); } -} -void TargetRegistersPaneWidget::onRegistersRead(const Targets::TargetRegisters& registers) { - for (const auto& targetRegister : registers) { - auto& descriptor = targetRegister.descriptor; + void TargetRegistersPaneWidget::onItemSelectionChange(ItemWidget* newlySelectedWidget) { + // Only one item in the target registers pane can be selected at any given time. + if (this->selectedItemWidget != newlySelectedWidget) { + if (this->selectedItemWidget != nullptr) { + this->selectedItemWidget->setSelected(false); + } - for (const auto& registerGroupWidget : this->registerGroupWidgets) { - if (registerGroupWidget->registerWidgetsMappedByDescriptor.contains(descriptor)) { - registerGroupWidget->registerWidgetsMappedByDescriptor.at(descriptor)->setRegisterValue(targetRegister); - break; + this->selectedItemWidget = newlySelectedWidget; + } + } + + void TargetRegistersPaneWidget::resizeEvent(QResizeEvent* event) { + const auto parentSize = this->parent->size(); + const auto width = parentSize.width() - 1; + this->container->setFixedWidth(width); + this->searchInput->setFixedWidth(width - 20); + + /* + * In order to avoid the panel resize handle overlapping the scroll bar handle, we reduce the size of + * the scroll area. + */ + this->itemScrollArea->setFixedWidth(width - this->parent->getHandleSize()); + } + + void TargetRegistersPaneWidget::postActivate() { + if (this->targetState == Targets::TargetState::STOPPED) { + this->refreshRegisterValues(); + } + } + + void TargetRegistersPaneWidget::postDeactivate() { + + } + + void TargetRegistersPaneWidget::onTargetStateChanged(Targets::TargetState newState) { + using Targets::TargetState; + this->targetState = newState; + + if (newState == TargetState::STOPPED && this->activated) { + this->refreshRegisterValues(); + } + } + + void TargetRegistersPaneWidget::onRegistersRead(const Targets::TargetRegisters& registers) { + for (const auto& targetRegister : registers) { + auto& descriptor = targetRegister.descriptor; + + for (const auto& registerGroupWidget : this->registerGroupWidgets) { + if (registerGroupWidget->registerWidgetsMappedByDescriptor.contains(descriptor)) { + registerGroupWidget->registerWidgetsMappedByDescriptor.at( + descriptor + )->setRegisterValue(targetRegister); + break; + } } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/BodyWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/BodyWidget.cpp index 3f16fbf3..d44c5e07 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/BodyWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/BodyWidget.cpp @@ -1,81 +1,85 @@ #include "BodyWidget.hpp" -using namespace Bloom::Widgets::InsightTargetWidgets::Dip; +namespace Bloom::Widgets::InsightTargetWidgets::Dip +{ + BodyWidget::BodyWidget(QWidget* parent, std::size_t pinCount): QWidget(parent) { + this->setObjectName("target-body"); -BodyWidget::BodyWidget(QWidget* parent, std::size_t pinCount): QWidget(parent) { - this->setObjectName("target-body"); - - /* - * The DIP package widget looks awkward when the body height is fixed. For this reason, the body height and - * indicator sizes are proportional to the pin count. - * - * The proportionality constants used below were chosen because they look the nicest. No other reason. - */ - this->setFixedHeight( - std::min( - BodyWidget::MAXIMUM_BODY_HEIGHT, - std::max( - BodyWidget::MINIMUM_BODY_HEIGHT, - static_cast(std::ceil(3.6 * static_cast(pinCount))) + /* + * The DIP package widget looks awkward when the body height is fixed. For this reason, the body height and + * indicator sizes are proportional to the pin count. + * + * The proportionality constants used below were chosen because they look the nicest. No other reason. + */ + this->setFixedHeight( + std::min( + BodyWidget::MAXIMUM_BODY_HEIGHT, + std::max( + BodyWidget::MINIMUM_BODY_HEIGHT, + static_cast(std::ceil(3.6 * static_cast(pinCount))) + ) ) - ) - ); + ); - this->firstPinIndicatorDiameter = std::min( - BodyWidget::MAXIMUM_FIRST_PIN_INDICATOR_HEIGHT, - std::max(BodyWidget::MINIMUM_FIRST_PIN_INDICATOR_HEIGHT, static_cast(std::floor(pinCount / 2))) - ); + this->firstPinIndicatorDiameter = std::min( + BodyWidget::MAXIMUM_FIRST_PIN_INDICATOR_HEIGHT, + std::max(BodyWidget::MINIMUM_FIRST_PIN_INDICATOR_HEIGHT, static_cast(std::floor(pinCount / 2))) + ); - this->orientationIndicatorDiameter = this->firstPinIndicatorDiameter % 2 == 0 ? - this->firstPinIndicatorDiameter + 3 - : this->firstPinIndicatorDiameter + 2; -} - -void BodyWidget::paintEvent(QPaintEvent* event) { - auto painter = QPainter(this); - this->drawWidget(painter); -} - -void BodyWidget::drawWidget(QPainter& painter) { - painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true); - const auto bodyHeight = this->height(); - const auto bodyRect = QRectF( - 0, - 0, - this->width(), - bodyHeight - ); - - // The first pin indicator is just a circle positioned close to the first pin - const auto firstPinIndicatorRect = QRectF( - 6, - (bodyHeight - this->firstPinIndicatorDiameter - 6), - this->firstPinIndicatorDiameter, - this->firstPinIndicatorDiameter - ); - - /* - * The orientation indicator is just a half-circle cut-out, positioned on the side of the DIP package closest to - * the first pin. - */ - const auto orientationIndicatorRect = QRectF( - -(this->orientationIndicatorDiameter / 2), - (bodyHeight / 2) - (this->orientationIndicatorDiameter / 2), - this->orientationIndicatorDiameter, - this->orientationIndicatorDiameter - ); - - static const auto backgroundColor = QColor(0x37, 0x38, 0x35); - auto targetBodyColor = this->getBodyColor(); - - if (!this->isEnabled()) { - targetBodyColor.setAlpha(this->getDisableAlphaLevel()); + this->orientationIndicatorDiameter = this->firstPinIndicatorDiameter % 2 == 0 ? + this->firstPinIndicatorDiameter + 3 + : this->firstPinIndicatorDiameter + 2; } - painter.setPen(Qt::PenStyle::NoPen); - painter.setBrush(targetBodyColor); - painter.drawRect(bodyRect); - painter.setBrush(backgroundColor); - painter.drawEllipse(firstPinIndicatorRect); - painter.drawEllipse(orientationIndicatorRect); + void BodyWidget::paintEvent(QPaintEvent* event) { + auto painter = QPainter(this); + this->drawWidget(painter); + } + + void BodyWidget::drawWidget(QPainter& painter) { + painter.setRenderHints( + QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, + true + ); + const auto bodyHeight = this->height(); + const auto bodyRect = QRectF( + 0, + 0, + this->width(), + bodyHeight + ); + + // The first pin indicator is just a circle positioned close to the first pin + const auto firstPinIndicatorRect = QRectF( + 6, + (bodyHeight - this->firstPinIndicatorDiameter - 6), + this->firstPinIndicatorDiameter, + this->firstPinIndicatorDiameter + ); + + /* + * The orientation indicator is just a half-circle cut-out, positioned on the side of the DIP package + * closest to the first pin. + */ + const auto orientationIndicatorRect = QRectF( + -(this->orientationIndicatorDiameter / 2), + (bodyHeight / 2) - (this->orientationIndicatorDiameter / 2), + this->orientationIndicatorDiameter, + this->orientationIndicatorDiameter + ); + + static const auto backgroundColor = QColor(0x37, 0x38, 0x35); + auto targetBodyColor = this->getBodyColor(); + + if (!this->isEnabled()) { + targetBodyColor.setAlpha(this->getDisableAlphaLevel()); + } + + painter.setPen(Qt::PenStyle::NoPen); + painter.setBrush(targetBodyColor); + painter.drawRect(bodyRect); + painter.setBrush(backgroundColor); + painter.drawEllipse(firstPinIndicatorRect); + painter.drawEllipse(orientationIndicatorRect); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/DualInlinePackageWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/DualInlinePackageWidget.cpp index c3d57834..d122f767 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/DualInlinePackageWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/DualInlinePackageWidget.cpp @@ -8,242 +8,245 @@ #include "src/Helpers/Paths.hpp" -using namespace Bloom::Widgets::InsightTargetWidgets::Dip; -using namespace Bloom::Exceptions; +namespace Bloom::Widgets::InsightTargetWidgets::Dip +{ + using namespace Bloom::Exceptions; -using Bloom::Targets::TargetVariant; + using Bloom::Targets::TargetVariant; -DualInlinePackageWidget::DualInlinePackageWidget( - const TargetVariant& targetVariant, - InsightWorker& insightWorker, - QWidget* parent -): TargetPackageWidget(targetVariant, insightWorker, parent) { - auto stylesheetFile = QFile(QString::fromStdString( - Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/Stylesheets/DualInlinePackage.qss" - ) - ); - stylesheetFile.open(QFile::ReadOnly); - this->setStyleSheet(stylesheetFile.readAll()); - - this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size()); - this->layout = new QVBoxLayout(); - this->layout->setSpacing(PinWidget::WIDTH_SPACING); - this->layout->setContentsMargins(0, 0, 0, 0); - this->layout->setAlignment(Qt::AlignmentFlag::AlignVCenter); - - this->topPinLayout = new QHBoxLayout(); - this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING); - this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft); - this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); - this->bottomPinLayout = new QHBoxLayout(); - this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING); - this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); - - for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) { - auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this); - this->pinWidgets.push_back(pinWidget); - TargetPackageWidget::pinWidgets.push_back(pinWidget); - - if (targetPinNumber <= (targetVariant.pinDescriptorsByNumber.size() / 2)) { - this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter); - } else { - this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter); - } - } - - this->bodyWidget = new BodyWidget(this, targetVariant.pinDescriptorsByNumber.size()); - - this->layout->addLayout(this->topPinLayout); - this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignVCenter); - this->layout->addLayout(this->bottomPinLayout); - this->setLayout(this->layout); - - const auto bodyWidgetHeight = this->bodyWidget->height(); - const auto bodyWidgetWidth = ((PinWidget::MINIMUM_WIDTH + PinWidget::WIDTH_SPACING) - * static_cast(this->pinWidgets.size() / 2)) - PinWidget::WIDTH_SPACING + 46; - - this->bodyWidget->setGeometry( - 0, - PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING, - bodyWidgetWidth, - bodyWidgetHeight - ); - - const auto width = bodyWidgetWidth; - const auto height = ( - ( - PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING + PinWidget::PIN_LABEL_LONG_LINE_LENGTH - + PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + ( - (PinWidget::LABEL_HEIGHT + PinWidget::PIN_LABEL_SPACING) * 2 + DualInlinePackageWidget::DualInlinePackageWidget( + const TargetVariant& targetVariant, + InsightWorker& insightWorker, + QWidget* parent + ): TargetPackageWidget(targetVariant, insightWorker, parent) { + auto stylesheetFile = QFile(QString::fromStdString( + Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/Stylesheets/" + "DualInlinePackage.qss" ) - ) * 2) + bodyWidgetHeight; + ); + stylesheetFile.open(QFile::ReadOnly); + this->setStyleSheet(stylesheetFile.readAll()); - this->topPinLayout->setGeometry(QRect(0, 0, width, PinWidget::MAXIMUM_HEIGHT)); - this->bottomPinLayout->setGeometry( - QRect( - 0, - (PinWidget::MAXIMUM_HEIGHT + bodyWidgetHeight + (PinWidget::WIDTH_SPACING * 2)), - width, - PinWidget::MAXIMUM_HEIGHT - ) - ); - this->topPinLayout->setContentsMargins(23, 0, 23, 0); - this->bottomPinLayout->setContentsMargins( 23, 0, 23, 0); + this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size()); + this->layout = new QVBoxLayout(); + this->layout->setSpacing(PinWidget::WIDTH_SPACING); + this->layout->setContentsMargins(0, 0, 0, 0); + this->layout->setAlignment(Qt::AlignmentFlag::AlignVCenter); - this->setFixedSize(width, height); + this->topPinLayout = new QHBoxLayout(); + this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING); + this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft); + this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); + this->bottomPinLayout = new QHBoxLayout(); + this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING); + this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); - this->setGeometry( - (parent->width() / 2) - (width / 2), - (parent->height() / 2) - (height / 2), - width, - height - ); -} + for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) { + auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this); + this->pinWidgets.push_back(pinWidget); + TargetPackageWidget::pinWidgets.push_back(pinWidget); -void DualInlinePackageWidget::paintEvent(QPaintEvent* event) { - auto painter = QPainter(this); - this->drawWidget(painter); -} - -void DualInlinePackageWidget::drawWidget(QPainter& painter) { - using Targets::TargetPinState; - - static auto pinNameFont = QFont("'Ubuntu', sans-serif"); - static auto pinDirectionFont = pinNameFont; - pinNameFont.setPixelSize(11); - pinDirectionFont.setPixelSize(9); - - static const auto lineColor = QColor(0x4F, 0x4F, 0x4F); - static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA); - static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D); - static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA); - - static const auto inDirectionText = QString("IN"); - static const auto outDirectionText = QString("OUT"); - - for (const auto* pinWidget : this->pinWidgets) { - const auto pinGeoPosition = pinWidget->pos(); - const auto& pinState = pinWidget->getPinState(); - const auto pinStateChanged = pinWidget->hasPinStateChanged(); - - painter.setFont(pinNameFont); - - if (pinWidget->position == Position::TOP) { - painter.setPen(lineColor); - const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - : PinWidget::PIN_LABEL_SHORT_LINE_LENGTH - ); - const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? - PinWidget::PIN_LABEL_SHORT_LINE_LENGTH - : PinWidget::PIN_LABEL_LONG_LINE_LENGTH - ); - - painter.drawLine(QLine( - pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), - pinGeoPosition.y() - pinNameLabelLineLength, - pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), - pinGeoPosition.y() - )); - - painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); - painter.drawText( - QRect( - pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2) - - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), - pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT, - PinWidget::MAXIMUM_LABEL_WIDTH, - PinWidget::MAXIMUM_LABEL_HEIGHT - ), - Qt::AlignCenter, - pinWidget->pinNameLabelText - ); - - if (pinState.has_value() && pinState->ioDirection.has_value()) { - painter.setFont(pinDirectionFont); - - painter.setPen(lineColor); - painter.drawLine(QLine( - pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), - pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT - - pinDirectionLabelLineLength, - pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), - pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT - )); - - painter.setPen(pinDirectionFontColor); - painter.drawText( - QRect( - pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2) - - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), - pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength - - (PinWidget::MAXIMUM_LABEL_HEIGHT * 2), - PinWidget::MAXIMUM_LABEL_WIDTH, - PinWidget::MAXIMUM_LABEL_HEIGHT - ), - Qt::AlignCenter, - pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText - ); + if (targetPinNumber <= (targetVariant.pinDescriptorsByNumber.size() / 2)) { + this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter); + } else { + this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter); } + } - } else if (pinWidget->position == Position::BOTTOM) { - painter.setPen(lineColor); - const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - : PinWidget::PIN_LABEL_SHORT_LINE_LENGTH - ); - const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? - PinWidget::PIN_LABEL_SHORT_LINE_LENGTH - : PinWidget::PIN_LABEL_LONG_LINE_LENGTH - ); + this->bodyWidget = new BodyWidget(this, targetVariant.pinDescriptorsByNumber.size()); - painter.drawLine(QLine( - pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), - pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT, - pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), - pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength - )); + this->layout->addLayout(this->topPinLayout); + this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignVCenter); + this->layout->addLayout(this->bottomPinLayout); + this->setLayout(this->layout); - painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); - painter.drawText( - QRect( - pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2) - - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), - pinGeoPosition.y() + + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength, - PinWidget::MAXIMUM_LABEL_WIDTH, - PinWidget::MAXIMUM_LABEL_HEIGHT - ), - Qt::AlignCenter, - pinWidget->pinNameLabelText - ); + const auto bodyWidgetHeight = this->bodyWidget->height(); + const auto bodyWidgetWidth = ((PinWidget::MINIMUM_WIDTH + PinWidget::WIDTH_SPACING) + * static_cast(this->pinWidgets.size() / 2)) - PinWidget::WIDTH_SPACING + 46; - if (pinState.has_value() && pinState->ioDirection.has_value()) { - painter.setFont(pinDirectionFont); + this->bodyWidget->setGeometry( + 0, + PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING, + bodyWidgetWidth, + bodyWidgetHeight + ); + const auto width = bodyWidgetWidth; + const auto height = ( + ( + PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + + PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + ( + (PinWidget::LABEL_HEIGHT + PinWidget::PIN_LABEL_SPACING) * 2 + ) + ) * 2) + bodyWidgetHeight; + + this->topPinLayout->setGeometry(QRect(0, 0, width, PinWidget::MAXIMUM_HEIGHT)); + this->bottomPinLayout->setGeometry( + QRect( + 0, + (PinWidget::MAXIMUM_HEIGHT + bodyWidgetHeight + (PinWidget::WIDTH_SPACING * 2)), + width, + PinWidget::MAXIMUM_HEIGHT + ) + ); + this->topPinLayout->setContentsMargins(23, 0, 23, 0); + this->bottomPinLayout->setContentsMargins( 23, 0, 23, 0); + + this->setFixedSize(width, height); + + this->setGeometry( + (parent->width() / 2) - (width / 2), + (parent->height() / 2) - (height / 2), + width, + height + ); + } + + void DualInlinePackageWidget::paintEvent(QPaintEvent* event) { + auto painter = QPainter(this); + this->drawWidget(painter); + } + + void DualInlinePackageWidget::drawWidget(QPainter& painter) { + using Targets::TargetPinState; + + static auto pinNameFont = QFont("'Ubuntu', sans-serif"); + static auto pinDirectionFont = pinNameFont; + pinNameFont.setPixelSize(11); + pinDirectionFont.setPixelSize(9); + + static const auto lineColor = QColor(0x4F, 0x4F, 0x4F); + static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA); + static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D); + static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA); + + static const auto inDirectionText = QString("IN"); + static const auto outDirectionText = QString("OUT"); + + for (const auto* pinWidget : this->pinWidgets) { + const auto pinGeoPosition = pinWidget->pos(); + const auto& pinState = pinWidget->getPinState(); + const auto pinStateChanged = pinWidget->hasPinStateChanged(); + + painter.setFont(pinNameFont); + + if (pinWidget->position == Position::TOP) { painter.setPen(lineColor); + const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + : PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + ); + const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? + PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + : PinWidget::PIN_LABEL_LONG_LINE_LENGTH + ); + painter.drawLine(QLine( pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), - pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength - + PinWidget::MAXIMUM_LABEL_HEIGHT, + pinGeoPosition.y() - pinNameLabelLineLength, pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), - pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength - + PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength + pinGeoPosition.y() )); - painter.setPen(pinDirectionFontColor); + painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); painter.drawText( QRect( pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2) - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), - pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength - + PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength, + pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT, PinWidget::MAXIMUM_LABEL_WIDTH, PinWidget::MAXIMUM_LABEL_HEIGHT ), Qt::AlignCenter, - pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText + pinWidget->pinNameLabelText ); + + if (pinState.has_value() && pinState->ioDirection.has_value()) { + painter.setFont(pinDirectionFont); + + painter.setPen(lineColor); + painter.drawLine(QLine( + pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), + pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT + - pinDirectionLabelLineLength, + pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), + pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT + )); + + painter.setPen(pinDirectionFontColor); + painter.drawText( + QRect( + pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2) + - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), + pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength + - (PinWidget::MAXIMUM_LABEL_HEIGHT * 2), + PinWidget::MAXIMUM_LABEL_WIDTH, + PinWidget::MAXIMUM_LABEL_HEIGHT + ), + Qt::AlignCenter, + pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText + ); + } + + } else if (pinWidget->position == Position::BOTTOM) { + painter.setPen(lineColor); + const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + : PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + ); + const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? + PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + : PinWidget::PIN_LABEL_LONG_LINE_LENGTH + ); + + painter.drawLine(QLine( + pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), + pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT, + pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), + pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength + )); + + painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); + painter.drawText( + QRect( + pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2) + - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), + pinGeoPosition.y() + + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength, + PinWidget::MAXIMUM_LABEL_WIDTH, + PinWidget::MAXIMUM_LABEL_HEIGHT + ), + Qt::AlignCenter, + pinWidget->pinNameLabelText + ); + + if (pinState.has_value() && pinState->ioDirection.has_value()) { + painter.setFont(pinDirectionFont); + + painter.setPen(lineColor); + painter.drawLine(QLine( + pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), + pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength + + PinWidget::MAXIMUM_LABEL_HEIGHT, + pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2), + pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength + + PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength + )); + + painter.setPen(pinDirectionFontColor); + painter.drawText( + QRect( + pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2) + - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), + pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength + + PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength, + PinWidget::MAXIMUM_LABEL_WIDTH, + PinWidget::MAXIMUM_LABEL_HEIGHT + ), + Qt::AlignCenter, + pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText + ); + } } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinBodyWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinBodyWidget.cpp index 2cbc24bf..4746c2ba 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinBodyWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinBodyWidget.cpp @@ -2,34 +2,39 @@ #include -using namespace Bloom::Widgets::InsightTargetWidgets::Dip; -using namespace Bloom::Targets; +namespace Bloom::Widgets::InsightTargetWidgets::Dip +{ + using namespace Bloom::Targets; -PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor) -: TargetPinBodyWidget(parent, std::move(pinDescriptor)) { - this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT); -} - -void PinBodyWidget::paintEvent(QPaintEvent* event) { - auto painter = QPainter(this); - this->drawWidget(painter); -} - -void PinBodyWidget::drawWidget(QPainter& painter) { - painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true); - auto pinWidth = PinBodyWidget::WIDTH; - auto pinHeight = PinBodyWidget::HEIGHT; - this->setFixedSize(pinWidth, pinHeight); - - auto pinColor = this->getBodyColor(); - - painter.setPen(Qt::PenStyle::NoPen); - painter.setBrush(pinColor); - - painter.drawRect( - 0, - 0, - pinWidth, - pinHeight - ); + PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor) + : TargetPinBodyWidget(parent, std::move(pinDescriptor)) { + this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT); + } + + void PinBodyWidget::paintEvent(QPaintEvent* event) { + auto painter = QPainter(this); + this->drawWidget(painter); + } + + void PinBodyWidget::drawWidget(QPainter& painter) { + painter.setRenderHints( + QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, + true + ); + auto pinWidth = PinBodyWidget::WIDTH; + auto pinHeight = PinBodyWidget::HEIGHT; + this->setFixedSize(pinWidth, pinHeight); + + auto pinColor = this->getBodyColor(); + + painter.setPen(Qt::PenStyle::NoPen); + painter.setBrush(pinColor); + + painter.drawRect( + 0, + 0, + pinWidth, + pinHeight + ); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinWidget.cpp index 0607bf2a..a0c9caa5 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinWidget.cpp @@ -1,47 +1,49 @@ #include "PinWidget.hpp" -using namespace Bloom::Widgets::InsightTargetWidgets::Dip; -using namespace Bloom::Targets; +namespace Bloom::Widgets::InsightTargetWidgets::Dip +{ + using namespace Bloom::Targets; -PinWidget::PinWidget( - const TargetPinDescriptor& pinDescriptor, - const TargetVariant& targetVariant, - InsightWorker& insightWorker, - QWidget* parent -): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) { - this->setFixedSize(PinWidget::MINIMUM_WIDTH, PinWidget::MAXIMUM_HEIGHT); + PinWidget::PinWidget( + const TargetPinDescriptor& pinDescriptor, + const TargetVariant& targetVariant, + InsightWorker& insightWorker, + QWidget* parent + ): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) { + this->setFixedSize(PinWidget::MINIMUM_WIDTH, PinWidget::MAXIMUM_HEIGHT); - this->layout = new QVBoxLayout(); - this->layout->setContentsMargins(0, 0, 0, 0); - this->layout->setSpacing(0); + this->layout = new QVBoxLayout(); + this->layout->setContentsMargins(0, 0, 0, 0); + this->layout->setSpacing(0); - this->bodyWidget = new PinBodyWidget(this, this->pinDescriptor); - this->position = (pinDescriptor.number > (targetVariant.pinDescriptorsByNumber.size() / 2)) - ? Position::TOP : Position::BOTTOM; + this->bodyWidget = new PinBodyWidget(this, this->pinDescriptor); + this->position = (pinDescriptor.number > (targetVariant.pinDescriptorsByNumber.size() / 2)) + ? Position::TOP : Position::BOTTOM; - const bool isTopWidget = this->position == Position::TOP; + const bool isTopWidget = this->position == Position::TOP; - this->layout->setAlignment(isTopWidget ? (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom) - : (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop)); + this->layout->setAlignment(isTopWidget ? (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom) + : (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop)); - this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper(); - this->pinNameLabelText.truncate(5); + this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper(); + this->pinNameLabelText.truncate(5); - this->pinNumberLabel = new QLabel(this); - this->pinNumberLabel->setObjectName("target-pin-number"); - this->pinNumberLabel->setText(QString::number(pinDescriptor.number)); - this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter); + this->pinNumberLabel = new QLabel(this); + this->pinNumberLabel->setObjectName("target-pin-number"); + this->pinNumberLabel->setText(QString::number(pinDescriptor.number)); + this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter); - if (isTopWidget) { - this->layout->setDirection(QBoxLayout::Direction::BottomToTop); + if (isTopWidget) { + this->layout->setDirection(QBoxLayout::Direction::BottomToTop); + } + + this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignHCenter); + this->layout->insertSpacing(1, PinWidget::PIN_LABEL_SPACING); + this->layout->addWidget(this->pinNumberLabel, 0, Qt::AlignmentFlag::AlignHCenter); + this->pinNumberLabel->setFixedHeight(PinWidget::LABEL_HEIGHT); + + this->setLayout(this->layout); + + connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked); } - - this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignHCenter); - this->layout->insertSpacing(1, PinWidget::PIN_LABEL_SPACING); - this->layout->addWidget(this->pinNumberLabel, 0, Qt::AlignmentFlag::AlignHCenter); - this->pinNumberLabel->setFixedHeight(PinWidget::LABEL_HEIGHT); - - this->setLayout(this->layout); - - connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/BodyWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/BodyWidget.cpp index 6dbe0dbf..44f83b06 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/BodyWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/BodyWidget.cpp @@ -2,47 +2,51 @@ #include -using namespace Bloom::Widgets::InsightTargetWidgets::Qfp; - -void BodyWidget::paintEvent(QPaintEvent* event) { - auto painter = QPainter(this); - this->drawWidget(painter); -} - -void BodyWidget::drawWidget(QPainter& painter) { - painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true); - - auto targetBodyColor = this->getBodyColor(); - auto backgroundColor = QColor("#373835"); - - if (!this->isEnabled()) { - targetBodyColor.setAlpha(this->getDisableAlphaLevel()); +namespace Bloom::Widgets::InsightTargetWidgets::Qfp +{ + void BodyWidget::paintEvent(QPaintEvent* event) { + auto painter = QPainter(this); + this->drawWidget(painter); } - painter.setPen(Qt::PenStyle::NoPen); - painter.setBrush(targetBodyColor); + void BodyWidget::drawWidget(QPainter& painter) { + painter.setRenderHints( + QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, + true + ); - const auto containerSize = this->size(); - const auto targetBodyWidth = containerSize.width() - 8; - const auto targetBodyHeight = containerSize.height() - 8; + auto targetBodyColor = this->getBodyColor(); + auto backgroundColor = QColor("#373835"); - auto targetBodyPoint = QPoint( - (containerSize.width() / 2) - (targetBodyWidth / 2), - (containerSize.height() / 2) - (targetBodyHeight / 2) - ); + if (!this->isEnabled()) { + targetBodyColor.setAlpha(this->getDisableAlphaLevel()); + } - painter.drawRect( - targetBodyPoint.x(), - targetBodyPoint.y(), - targetBodyWidth, - targetBodyHeight - ); + painter.setPen(Qt::PenStyle::NoPen); + painter.setBrush(targetBodyColor); - painter.setBrush(backgroundColor); - painter.drawEllipse(QRectF( - targetBodyPoint.x() + 13, - targetBodyPoint.y() + 13, - 18, - 18 - )); + const auto containerSize = this->size(); + const auto targetBodyWidth = containerSize.width() - 8; + const auto targetBodyHeight = containerSize.height() - 8; + + auto targetBodyPoint = QPoint( + (containerSize.width() / 2) - (targetBodyWidth / 2), + (containerSize.height() / 2) - (targetBodyHeight / 2) + ); + + painter.drawRect( + targetBodyPoint.x(), + targetBodyPoint.y(), + targetBodyWidth, + targetBodyHeight + ); + + painter.setBrush(backgroundColor); + painter.drawEllipse(QRectF( + targetBodyPoint.x() + 13, + targetBodyPoint.y() + 13, + 18, + 18 + )); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinBodyWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinBodyWidget.cpp index b741a7f0..25a339d6 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinBodyWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinBodyWidget.cpp @@ -3,39 +3,44 @@ #include #include -using namespace Bloom::Widgets::InsightTargetWidgets::Qfp; -using namespace Bloom::Targets; +namespace Bloom::Widgets::InsightTargetWidgets::Qfp +{ + using namespace Bloom::Targets; -PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor, bool isVertical) -: TargetPinBodyWidget(parent, std::move(pinDescriptor)), isVertical(isVertical) { - if (isVertical) { - this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT); + PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor, bool isVertical) + : TargetPinBodyWidget(parent, std::move(pinDescriptor)), isVertical(isVertical) { + if (isVertical) { + this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT); - } else { - this->setFixedSize(PinBodyWidget::HEIGHT, PinBodyWidget::WIDTH); + } else { + this->setFixedSize(PinBodyWidget::HEIGHT, PinBodyWidget::WIDTH); + } + } + + void PinBodyWidget::paintEvent(QPaintEvent* event) { + auto painter = QPainter(this); + this->drawWidget(painter); + } + + void PinBodyWidget::drawWidget(QPainter& painter) { + painter.setRenderHints( + QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, + true + ); + auto pinWidth = this->isVertical ? PinBodyWidget::WIDTH : PinBodyWidget::HEIGHT; + auto pinHeight = this->isVertical ? PinBodyWidget::HEIGHT : PinBodyWidget::WIDTH; + this->setFixedSize(pinWidth, pinHeight); + + auto pinColor = this->getBodyColor(); + + painter.setPen(Qt::PenStyle::NoPen); + painter.setBrush(pinColor); + + painter.drawRect( + 0, + 0, + pinWidth, + pinHeight + ); } } - -void PinBodyWidget::paintEvent(QPaintEvent* event) { - auto painter = QPainter(this); - this->drawWidget(painter); -} - -void PinBodyWidget::drawWidget(QPainter& painter) { - painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true); - auto pinWidth = this->isVertical ? PinBodyWidget::WIDTH : PinBodyWidget::HEIGHT; - auto pinHeight = this->isVertical ? PinBodyWidget::HEIGHT : PinBodyWidget::WIDTH; - this->setFixedSize(pinWidth, pinHeight); - - auto pinColor = this->getBodyColor(); - - painter.setPen(Qt::PenStyle::NoPen); - painter.setBrush(pinColor); - - painter.drawRect( - 0, - 0, - pinWidth, - pinHeight - ); -} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinWidget.cpp index 6122b834..b6a99edf 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinWidget.cpp @@ -7,95 +7,97 @@ #include "PinWidget.hpp" #include "PinBodyWidget.hpp" -using namespace Bloom::Widgets::InsightTargetWidgets::Qfp; -using namespace Bloom::Targets; +namespace Bloom::Widgets::InsightTargetWidgets::Qfp +{ + using namespace Bloom::Targets; -PinWidget::PinWidget( - const TargetPinDescriptor& pinDescriptor, - const TargetVariant& targetVariant, - InsightWorker& insightWorker, - QWidget* parent -): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) { - this->layout = new QBoxLayout(QBoxLayout::TopToBottom); - this->layout->setContentsMargins(0, 0, 0, 0); - this->layout->setSpacing(0); + PinWidget::PinWidget( + const TargetPinDescriptor& pinDescriptor, + const TargetVariant& targetVariant, + InsightWorker& insightWorker, + QWidget* parent + ): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) { + this->layout = new QBoxLayout(QBoxLayout::TopToBottom); + this->layout->setContentsMargins(0, 0, 0, 0); + this->layout->setSpacing(0); - auto pinCountPerLayout = (targetVariant.pinDescriptorsByNumber.size() / 4); + auto pinCountPerLayout = (targetVariant.pinDescriptorsByNumber.size() / 4); - if (pinDescriptor.number <= pinCountPerLayout) { - this->position = Position::LEFT; + if (pinDescriptor.number <= pinCountPerLayout) { + this->position = Position::LEFT; - } else if (pinDescriptor.number > pinCountPerLayout && pinDescriptor.number <= (pinCountPerLayout * 2)) { - this->position = Position::BOTTOM; + } else if (pinDescriptor.number > pinCountPerLayout && pinDescriptor.number <= (pinCountPerLayout * 2)) { + this->position = Position::BOTTOM; - } else if (pinDescriptor.number > (pinCountPerLayout * 2) && pinDescriptor.number <= (pinCountPerLayout * 3)) { - this->position = Position::RIGHT; + } else if (pinDescriptor.number > (pinCountPerLayout * 2) && pinDescriptor.number <= (pinCountPerLayout * 3)) { + this->position = Position::RIGHT; - } else if (pinDescriptor.number > (pinCountPerLayout * 3) && pinDescriptor.number <= (pinCountPerLayout * 4)) { - this->position = Position::TOP; - } + } else if (pinDescriptor.number > (pinCountPerLayout * 3) && pinDescriptor.number <= (pinCountPerLayout * 4)) { + this->position = Position::TOP; + } - this->bodyWidget = new PinBodyWidget( - this, - this->pinDescriptor, - (this->position == Position::TOP || this->position == Position::BOTTOM) - ); - - this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper(); - this->pinNameLabelText.truncate(5); - - this->pinNumberLabel = new QLabel(this); - this->pinNumberLabel->setObjectName("target-pin-number"); - auto pinNumberText = QString::number(pinDescriptor.number); - pinNumberText.truncate(5); - this->pinNumberLabel->setText(QString::number(pinDescriptor.number)); - this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter); - - if (this->position == Position::LEFT) { - this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignRight)); - this->layout->setDirection(QBoxLayout::Direction::RightToLeft); - this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT); - - } else if (this->position == Position::BOTTOM) { - this->layout->setAlignment(Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop); - this->layout->setDirection(QBoxLayout::Direction::TopToBottom); - this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT); - - } else if (this->position == Position::RIGHT) { - this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft)); - this->layout->setDirection(QBoxLayout::Direction::LeftToRight); - this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT); - - } else if (this->position == Position::TOP) { - this->layout->setAlignment((Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom)); - this->layout->setDirection(QBoxLayout::Direction::BottomToTop); - this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT); - } - - this->layout->addWidget(this->bodyWidget); - this->layout->addSpacing(3); - this->layout->addWidget(this->pinNumberLabel); - - if (this->position == Position::LEFT || this->position == Position::RIGHT) { - this->pinNumberLabel->setFixedSize( - PinWidget::MAXIMUM_PIN_NUMBER_LABEL_WIDTH, - PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2 + this->bodyWidget = new PinBodyWidget( + this, + this->pinDescriptor, + (this->position == Position::TOP || this->position == Position::BOTTOM) ); - } else if (this->position == Position::TOP || this->position == Position::BOTTOM) { - this->pinNumberLabel->setFixedSize(PinBodyWidget::WIDTH, PinWidget::LABEL_HEIGHT - 2); + this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper(); + this->pinNameLabelText.truncate(5); + + this->pinNumberLabel = new QLabel(this); + this->pinNumberLabel->setObjectName("target-pin-number"); + auto pinNumberText = QString::number(pinDescriptor.number); + pinNumberText.truncate(5); + this->pinNumberLabel->setText(QString::number(pinDescriptor.number)); + this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter); + + if (this->position == Position::LEFT) { + this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignRight)); + this->layout->setDirection(QBoxLayout::Direction::RightToLeft); + this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT); + + } else if (this->position == Position::BOTTOM) { + this->layout->setAlignment(Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop); + this->layout->setDirection(QBoxLayout::Direction::TopToBottom); + this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT); + + } else if (this->position == Position::RIGHT) { + this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft)); + this->layout->setDirection(QBoxLayout::Direction::LeftToRight); + this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT); + + } else if (this->position == Position::TOP) { + this->layout->setAlignment((Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom)); + this->layout->setDirection(QBoxLayout::Direction::BottomToTop); + this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT); + } + + this->layout->addWidget(this->bodyWidget); + this->layout->addSpacing(3); + this->layout->addWidget(this->pinNumberLabel); + + if (this->position == Position::LEFT || this->position == Position::RIGHT) { + this->pinNumberLabel->setFixedSize( + PinWidget::MAXIMUM_PIN_NUMBER_LABEL_WIDTH, + PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2 + ); + + } else if (this->position == Position::TOP || this->position == Position::BOTTOM) { + this->pinNumberLabel->setFixedSize(PinBodyWidget::WIDTH, PinWidget::LABEL_HEIGHT - 2); + } + + this->layout->addStretch(1); + this->setLayout(this->layout); + + connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked); } - this->layout->addStretch(1); - this->setLayout(this->layout); + void PinWidget::updatePinState(const Targets::TargetPinState& pinState) { + TargetPinWidget::updatePinState(pinState); - connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked); -} - -void PinWidget::updatePinState(const Targets::TargetPinState& pinState) { - TargetPinWidget::updatePinState(pinState); - - if (this->bodyWidget != nullptr) { - this->bodyWidget->setPinState(pinState); + if (this->bodyWidget != nullptr) { + this->bodyWidget->setPinState(pinState); + } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/QuadFlatPackageWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/QuadFlatPackageWidget.cpp index bd674b0e..b363c987 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/QuadFlatPackageWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/QuadFlatPackageWidget.cpp @@ -11,403 +11,407 @@ #include "PinWidget.hpp" #include "BodyWidget.hpp" -using namespace Bloom::Widgets::InsightTargetWidgets::Qfp; -using namespace Bloom::Targets; +namespace Bloom::Widgets::InsightTargetWidgets::Qfp +{ + using namespace Bloom::Targets; -QuadFlatPackageWidget::QuadFlatPackageWidget( - const TargetVariant& targetVariant, - InsightWorker& insightWorker, - QWidget* parent -): TargetPackageWidget(targetVariant, insightWorker, parent) { - assert((targetVariant.pinDescriptorsByNumber.size() % 4) == 0); + QuadFlatPackageWidget::QuadFlatPackageWidget( + const TargetVariant& targetVariant, + InsightWorker& insightWorker, + QWidget* parent + ): TargetPackageWidget(targetVariant, insightWorker, parent) { + assert((targetVariant.pinDescriptorsByNumber.size() % 4) == 0); - auto stylesheetFile = QFile(QString::fromStdString( - Paths::compiledResourcesPath() - + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/Stylesheets/QuadFlatPackage.qss" - ) - ); - stylesheetFile.open(QFile::ReadOnly); - this->setStyleSheet(stylesheetFile.readAll()); + auto stylesheetFile = QFile(QString::fromStdString( + Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/Stylesheets/QuadFlatPackage.qss" + ) + ); + stylesheetFile.open(QFile::ReadOnly); + this->setStyleSheet(stylesheetFile.readAll()); - this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size()); - this->layout = new QVBoxLayout(); - this->layout->setSpacing(0); - this->layout->setContentsMargins(0, 0, 0, 0); + this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size()); + this->layout = new QVBoxLayout(); + this->layout->setSpacing(0); + this->layout->setContentsMargins(0, 0, 0, 0); - this->horizontalLayout = new QHBoxLayout(); - this->horizontalLayout->setSpacing(0); - this->horizontalLayout->setDirection(QBoxLayout::Direction::LeftToRight); - this->horizontalLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); + this->horizontalLayout = new QHBoxLayout(); + this->horizontalLayout->setSpacing(0); + this->horizontalLayout->setDirection(QBoxLayout::Direction::LeftToRight); + this->horizontalLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); - this->topPinLayout = new QHBoxLayout(); - this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING); - this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft); - this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); + this->topPinLayout = new QHBoxLayout(); + this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING); + this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft); + this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); - this->rightPinLayout = new QVBoxLayout(); - this->rightPinLayout->setSpacing(PinWidget::WIDTH_SPACING); - this->rightPinLayout->setDirection(QBoxLayout::Direction::BottomToTop); - this->rightPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter); + this->rightPinLayout = new QVBoxLayout(); + this->rightPinLayout->setSpacing(PinWidget::WIDTH_SPACING); + this->rightPinLayout->setDirection(QBoxLayout::Direction::BottomToTop); + this->rightPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter); - this->bottomPinLayout = new QHBoxLayout(); - this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING); - this->bottomPinLayout->setDirection(QBoxLayout::Direction::LeftToRight); - this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); + this->bottomPinLayout = new QHBoxLayout(); + this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING); + this->bottomPinLayout->setDirection(QBoxLayout::Direction::LeftToRight); + this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); - this->leftPinLayout = new QVBoxLayout(); - this->leftPinLayout->setSpacing(PinWidget::WIDTH_SPACING); - this->leftPinLayout->setDirection(QBoxLayout::Direction::TopToBottom); - this->leftPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter); + this->leftPinLayout = new QVBoxLayout(); + this->leftPinLayout->setSpacing(PinWidget::WIDTH_SPACING); + this->leftPinLayout->setDirection(QBoxLayout::Direction::TopToBottom); + this->leftPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter); - const auto pinCountPerLayout = static_cast(targetVariant.pinDescriptorsByNumber.size() / 4); - for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) { - auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this); - this->pinWidgets.push_back(pinWidget); - TargetPackageWidget::pinWidgets.push_back(pinWidget); + const auto pinCountPerLayout = static_cast(targetVariant.pinDescriptorsByNumber.size() / 4); + for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) { + auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this); + this->pinWidgets.push_back(pinWidget); + TargetPackageWidget::pinWidgets.push_back(pinWidget); - if (pinWidget->position == Position::LEFT) { - this->leftPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignRight); + if (pinWidget->position == Position::LEFT) { + this->leftPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignRight); - } else if (pinWidget->position == Position::BOTTOM) { - this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignTop); + } else if (pinWidget->position == Position::BOTTOM) { + this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignTop); - } else if (pinWidget->position == Position::RIGHT) { - this->rightPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignLeft); + } else if (pinWidget->position == Position::RIGHT) { + this->rightPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignLeft); - } else if (pinWidget->position == Position::TOP) { - this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignBottom); + } else if (pinWidget->position == Position::TOP) { + this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignBottom); + } } + + this->bodyWidget = new BodyWidget(this); + this->layout->addLayout(this->topPinLayout); + this->horizontalLayout->addLayout(this->leftPinLayout); + this->horizontalLayout->addWidget(this->bodyWidget); + this->horizontalLayout->addLayout(this->rightPinLayout); + this->layout->addLayout(this->horizontalLayout); + this->layout->addLayout(this->bottomPinLayout); + this->setLayout(this->layout); + + // Layout sizing, positioning and padding + const auto verticalPinWidgetHeight = PinWidget::MAXIMUM_VERTICAL_HEIGHT; + const auto verticalPinWidgetWidth = PinWidget::MAXIMUM_VERTICAL_WIDTH; + const auto horizontalPinWidgetHeight = PinWidget::MAXIMUM_HORIZONTAL_HEIGHT; + const auto horizontalPinWidgetWidth = PinWidget::MAXIMUM_HORIZONTAL_WIDTH; + + /* + * Horizontal layouts are the right and left pin layouts - the ones that hold horizontal pin widgets. + * The bottom and top layouts are vertical layouts, as they hold the vertical pin widgets. + */ + const auto horizontalLayoutHeight = ((horizontalPinWidgetHeight + PinWidget::WIDTH_SPACING) * pinCountPerLayout + + PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING); + + const auto verticalLayoutWidth = ((verticalPinWidgetWidth + PinWidget::WIDTH_SPACING) * pinCountPerLayout + + PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING); + + const auto height = horizontalLayoutHeight + (verticalPinWidgetHeight * 2) + ( + ( + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + + PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + + (PinWidget::MAXIMUM_LABEL_HEIGHT * 2) + + (PinWidget::PIN_LABEL_SPACING * 3) + ) * 2 + ); + const auto width = verticalLayoutWidth + (horizontalPinWidgetWidth * 2) + ( + ( + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + + PinWidget::MAXIMUM_LABEL_WIDTH + + PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH + + (PinWidget::PIN_LABEL_SPACING * 2) + ) * 2 + ); + + this->topPinLayout->insertSpacing(0, horizontalPinWidgetWidth); + this->topPinLayout->addSpacing(horizontalPinWidgetWidth); + + this->bottomPinLayout->insertSpacing(0, horizontalPinWidgetWidth); + this->bottomPinLayout->addSpacing(horizontalPinWidgetWidth); + + this->leftPinLayout->setGeometry(QRect( + 0, + verticalPinWidgetHeight, + horizontalPinWidgetWidth, + horizontalLayoutHeight + )); + + this->bodyWidget->setFixedSize(verticalLayoutWidth, horizontalLayoutHeight); + + this->rightPinLayout->setGeometry(QRect( + horizontalLayoutHeight + horizontalPinWidgetWidth, + verticalPinWidgetHeight, + horizontalPinWidgetWidth, + horizontalLayoutHeight + )); + + const auto pinWidgetLayoutMargin = PinWidget::PIN_WIDGET_LAYOUT_PADDING / 2; + + this->topPinLayout->setContentsMargins( + pinWidgetLayoutMargin, + 0, + pinWidgetLayoutMargin, + 0 + ); + + this->bottomPinLayout->setContentsMargins( + pinWidgetLayoutMargin, + 0, + pinWidgetLayoutMargin, + 0 + ); + + this->leftPinLayout->setContentsMargins( + 0, + pinWidgetLayoutMargin, + 0, + pinWidgetLayoutMargin + ); + + this->rightPinLayout->setContentsMargins( + 0, + pinWidgetLayoutMargin, + 0, + pinWidgetLayoutMargin + ); + + this->setFixedSize(width, height); + + // Set the fixed size and center the widget + this->setGeometry( + (parent->width() / 2) - (width / 2), + (parent->height() / 2) - (height / 2), + width, + height + ); } - this->bodyWidget = new BodyWidget(this); - this->layout->addLayout(this->topPinLayout); - this->horizontalLayout->addLayout(this->leftPinLayout); - this->horizontalLayout->addWidget(this->bodyWidget); - this->horizontalLayout->addLayout(this->rightPinLayout); - this->layout->addLayout(this->horizontalLayout); - this->layout->addLayout(this->bottomPinLayout); - this->setLayout(this->layout); + void QuadFlatPackageWidget::paintEvent(QPaintEvent* event) { + auto painter = QPainter(this); + this->drawWidget(painter); + } - // Layout sizing, positioning and padding - const auto verticalPinWidgetHeight = PinWidget::MAXIMUM_VERTICAL_HEIGHT; - const auto verticalPinWidgetWidth = PinWidget::MAXIMUM_VERTICAL_WIDTH; - const auto horizontalPinWidgetHeight = PinWidget::MAXIMUM_HORIZONTAL_HEIGHT; - const auto horizontalPinWidgetWidth = PinWidget::MAXIMUM_HORIZONTAL_WIDTH; + void QuadFlatPackageWidget::drawWidget(QPainter& painter) { + static auto pinNameFont = QFont("'Ubuntu', sans-serif"); + static auto pinDirectionFont = pinNameFont; + pinNameFont.setPixelSize(11); + pinDirectionFont.setPixelSize(9); - /* - * Horizontal layouts are the right and left pin layouts - the ones that hold horizontal pin widgets. - * The bottom and top layouts are vertical layouts, as they hold the vertical pin widgets. - */ - const auto horizontalLayoutHeight = ((horizontalPinWidgetHeight + PinWidget::WIDTH_SPACING) * pinCountPerLayout - + PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING); + static const auto lineColor = QColor(0x4F, 0x4F, 0x4F); + static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA); + static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D); + static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA); - const auto verticalLayoutWidth = ((verticalPinWidgetWidth + PinWidget::WIDTH_SPACING) * pinCountPerLayout - + PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING); + static const auto inDirectionText = QString("IN"); + static const auto outDirectionText = QString("OUT"); - const auto height = horizontalLayoutHeight + (verticalPinWidgetHeight * 2) + ( - ( - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - + PinWidget::PIN_LABEL_SHORT_LINE_LENGTH - + (PinWidget::MAXIMUM_LABEL_HEIGHT * 2) - + (PinWidget::PIN_LABEL_SPACING * 3) - ) * 2 - ); - const auto width = verticalLayoutWidth + (horizontalPinWidgetWidth * 2) + ( - ( - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - + PinWidget::MAXIMUM_LABEL_WIDTH - + PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH - + (PinWidget::PIN_LABEL_SPACING * 2) - ) * 2 - ); + for (const auto* pinWidget : this->pinWidgets) { + const auto pinGeoPosition = pinWidget->pos(); + const auto& pinState = pinWidget->getPinState(); + const auto pinStateChanged = pinWidget->hasPinStateChanged(); - this->topPinLayout->insertSpacing(0, horizontalPinWidgetWidth); - this->topPinLayout->addSpacing(horizontalPinWidgetWidth); + painter.setFont(pinNameFont); - this->bottomPinLayout->insertSpacing(0, horizontalPinWidgetWidth); - this->bottomPinLayout->addSpacing(horizontalPinWidgetWidth); + if (pinWidget->position == Position::LEFT) { + painter.setPen(lineColor); + painter.drawLine(QLine( + pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH, + pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2), + pinGeoPosition.x(), + pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2) + )); - this->leftPinLayout->setGeometry(QRect( - 0, - verticalPinWidgetHeight, - horizontalPinWidgetWidth, - horizontalLayoutHeight - )); - - this->bodyWidget->setFixedSize(verticalLayoutWidth, horizontalLayoutHeight); - - this->rightPinLayout->setGeometry(QRect( - horizontalLayoutHeight + horizontalPinWidgetWidth, - verticalPinWidgetHeight, - horizontalPinWidgetWidth, - horizontalLayoutHeight - )); - - const auto pinWidgetLayoutMargin = PinWidget::PIN_WIDGET_LAYOUT_PADDING / 2; - - this->topPinLayout->setContentsMargins( - pinWidgetLayoutMargin, - 0, - pinWidgetLayoutMargin, - 0 - ); - - this->bottomPinLayout->setContentsMargins( - pinWidgetLayoutMargin, - 0, - pinWidgetLayoutMargin, - 0 - ); - - this->leftPinLayout->setContentsMargins( - 0, - pinWidgetLayoutMargin, - 0, - pinWidgetLayoutMargin - ); - - this->rightPinLayout->setContentsMargins( - 0, - pinWidgetLayoutMargin, - 0, - pinWidgetLayoutMargin - ); - - this->setFixedSize(width, height); - - // Set the fixed size and center the widget - this->setGeometry( - (parent->width() / 2) - (width / 2), - (parent->height() / 2) - (height / 2), - width, - height - ); -} - -void QuadFlatPackageWidget::paintEvent(QPaintEvent* event) { - auto painter = QPainter(this); - this->drawWidget(painter); -} - -void QuadFlatPackageWidget::drawWidget(QPainter& painter) { - static auto pinNameFont = QFont("'Ubuntu', sans-serif"); - static auto pinDirectionFont = pinNameFont; - pinNameFont.setPixelSize(11); - pinDirectionFont.setPixelSize(9); - - static const auto lineColor = QColor(0x4F, 0x4F, 0x4F); - static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA); - static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D); - static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA); - - static const auto inDirectionText = QString("IN"); - static const auto outDirectionText = QString("OUT"); - - for (const auto* pinWidget : this->pinWidgets) { - const auto pinGeoPosition = pinWidget->pos(); - const auto& pinState = pinWidget->getPinState(); - const auto pinStateChanged = pinWidget->hasPinStateChanged(); - - painter.setFont(pinNameFont); - - if (pinWidget->position == Position::LEFT) { - painter.setPen(lineColor); - painter.drawLine(QLine( - pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH, - pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2), - pinGeoPosition.x(), - pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2) - )); - - painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); - painter.drawText( - QRect( - pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - PinWidget::MAXIMUM_LABEL_WIDTH - - (PinWidget::PIN_LABEL_SPACING * 2), - pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) - - (PinWidget::MAXIMUM_LABEL_HEIGHT / 2), - PinWidget::MAXIMUM_LABEL_WIDTH, - PinWidget::MAXIMUM_LABEL_HEIGHT - ), - Qt::AlignCenter, - pinWidget->pinNameLabelText - ); - - if (pinState.has_value() && pinState->ioDirection.has_value()) { - painter.setFont(pinDirectionFont); - - painter.setPen(pinDirectionFontColor); + painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); painter.drawText( QRect( - pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - PinWidget::MAXIMUM_LABEL_WIDTH - - (PinWidget::PIN_LABEL_SPACING * 3) - PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH, + pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH + - PinWidget::MAXIMUM_LABEL_WIDTH - (PinWidget::PIN_LABEL_SPACING * 2), pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) - (PinWidget::MAXIMUM_LABEL_HEIGHT / 2), - PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH, + PinWidget::MAXIMUM_LABEL_WIDTH, PinWidget::MAXIMUM_LABEL_HEIGHT ), Qt::AlignCenter, - pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText + pinWidget->pinNameLabelText ); - } - } else if (pinWidget->position == Position::RIGHT) { - painter.setPen(lineColor); - painter.drawLine(QLine( - pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH, - pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), - pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH + PinWidget::PIN_LABEL_LONG_LINE_LENGTH, - pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) - )); + if (pinState.has_value() && pinState->ioDirection.has_value()) { + painter.setFont(pinDirectionFont); - painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); - painter.drawText( - QRect( + painter.setPen(pinDirectionFontColor); + painter.drawText( + QRect( + pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH + - PinWidget::MAXIMUM_LABEL_WIDTH - (PinWidget::PIN_LABEL_SPACING * 3) + - PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH, + pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) + - (PinWidget::MAXIMUM_LABEL_HEIGHT / 2), + PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH, + PinWidget::MAXIMUM_LABEL_HEIGHT + ), + Qt::AlignCenter, + pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText + ); + } + + } else if (pinWidget->position == Position::RIGHT) { + painter.setPen(lineColor); + painter.drawLine(QLine( + pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH, + pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH - + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + 8, + + PinWidget::PIN_LABEL_LONG_LINE_LENGTH, pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) - - (PinWidget::MAXIMUM_LABEL_HEIGHT / 2), - PinWidget::MAXIMUM_LABEL_WIDTH, - PinWidget::MAXIMUM_LABEL_HEIGHT - ), - Qt::AlignCenter, - pinWidget->pinNameLabelText - ); + )); - if (pinState.has_value() && pinState->ioDirection.has_value()) { - painter.setFont(pinDirectionFont); - - painter.setPen(pinDirectionFontColor); + painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); painter.drawText( QRect( pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH - + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + PinWidget::MAXIMUM_LABEL_WIDTH - + (PinWidget::PIN_LABEL_SPACING * 3), + + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + 8, pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) - (PinWidget::MAXIMUM_LABEL_HEIGHT / 2), - PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH, - PinWidget::MAXIMUM_LABEL_HEIGHT - ), - Qt::AlignCenter, - pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText - ); - } - - } else if (pinWidget->position == Position::TOP) { - painter.setPen(lineColor); - const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - : PinWidget::PIN_LABEL_SHORT_LINE_LENGTH - ); - const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? - PinWidget::PIN_LABEL_SHORT_LINE_LENGTH - : PinWidget::PIN_LABEL_LONG_LINE_LENGTH - ); - - painter.drawLine(QLine( - pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), - pinGeoPosition.y() - pinNameLabelLineLength, - pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), - pinGeoPosition.y() - )); - - painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); - painter.drawText( - QRect( - pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) - - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), - pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT, - PinWidget::MAXIMUM_LABEL_WIDTH, - PinWidget::MAXIMUM_LABEL_HEIGHT - ), - Qt::AlignCenter, - pinWidget->pinNameLabelText - ); - - if (pinState.has_value() && pinState->ioDirection.has_value()) { - painter.setFont(pinDirectionFont); - - painter.setPen(lineColor); - painter.drawLine(QLine( - pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), - pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT - - pinDirectionLabelLineLength, - pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), - pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT - )); - - painter.setPen(pinDirectionFontColor); - painter.drawText( - QRect( - pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) - - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), - pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength - - (PinWidget::MAXIMUM_LABEL_HEIGHT * 2), PinWidget::MAXIMUM_LABEL_WIDTH, PinWidget::MAXIMUM_LABEL_HEIGHT ), Qt::AlignCenter, - pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText + pinWidget->pinNameLabelText ); - } - } else if (pinWidget->position == Position::BOTTOM) { - painter.setPen(lineColor); - const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - : PinWidget::PIN_LABEL_SHORT_LINE_LENGTH - ); - const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? - PinWidget::PIN_LABEL_SHORT_LINE_LENGTH - : PinWidget::PIN_LABEL_LONG_LINE_LENGTH - ); + if (pinState.has_value() && pinState->ioDirection.has_value()) { + painter.setFont(pinDirectionFont); - painter.drawLine(QLine( - pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), - pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT, - pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), - pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength - )); - - painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); - painter.drawText( - QRect( - pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) - - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), - pinGeoPosition.y() + + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength, - PinWidget::MAXIMUM_LABEL_WIDTH, - PinWidget::MAXIMUM_LABEL_HEIGHT - ), - Qt::AlignCenter, - pinWidget->pinNameLabelText - ); - - if (pinState.has_value() && pinState->ioDirection.has_value()) { - painter.setFont(pinDirectionFont); + painter.setPen(pinDirectionFontColor); + painter.drawText( + QRect( + pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH + + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + PinWidget::MAXIMUM_LABEL_WIDTH + + (PinWidget::PIN_LABEL_SPACING * 3), + pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) + - (PinWidget::MAXIMUM_LABEL_HEIGHT / 2), + PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH, + PinWidget::MAXIMUM_LABEL_HEIGHT + ), + Qt::AlignCenter, + pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText + ); + } + } else if (pinWidget->position == Position::TOP) { painter.setPen(lineColor); + const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + : PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + ); + const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? + PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + : PinWidget::PIN_LABEL_LONG_LINE_LENGTH + ); + painter.drawLine(QLine( pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), - pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength - + PinWidget::MAXIMUM_LABEL_HEIGHT, + pinGeoPosition.y() - pinNameLabelLineLength, pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), - pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength - + PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength + pinGeoPosition.y() )); - painter.setPen(pinDirectionFontColor); + painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); painter.drawText( QRect( pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), + pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT, + PinWidget::MAXIMUM_LABEL_WIDTH, + PinWidget::MAXIMUM_LABEL_HEIGHT + ), + Qt::AlignCenter, + pinWidget->pinNameLabelText + ); + + if (pinState.has_value() && pinState->ioDirection.has_value()) { + painter.setFont(pinDirectionFont); + + painter.setPen(lineColor); + painter.drawLine(QLine( + pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), + pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT + - pinDirectionLabelLineLength, + pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), + pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT + )); + + painter.setPen(pinDirectionFontColor); + painter.drawText( + QRect( + pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) + - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), + pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength + - (PinWidget::MAXIMUM_LABEL_HEIGHT * 2), + PinWidget::MAXIMUM_LABEL_WIDTH, + PinWidget::MAXIMUM_LABEL_HEIGHT + ), + Qt::AlignCenter, + pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText + ); + } + + } else if (pinWidget->position == Position::BOTTOM) { + painter.setPen(lineColor); + const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? + PinWidget::PIN_LABEL_LONG_LINE_LENGTH + : PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + ); + const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ? + PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + : PinWidget::PIN_LABEL_LONG_LINE_LENGTH + ); + + painter.drawLine(QLine( + pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), + pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT, + pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), + pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength + )); + + painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor); + painter.drawText( + QRect( + pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) + - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), + pinGeoPosition.y() + + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength, + PinWidget::MAXIMUM_LABEL_WIDTH, + PinWidget::MAXIMUM_LABEL_HEIGHT + ), + Qt::AlignCenter, + pinWidget->pinNameLabelText + ); + + if (pinState.has_value() && pinState->ioDirection.has_value()) { + painter.setFont(pinDirectionFont); + + painter.setPen(lineColor); + painter.drawLine(QLine( + pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength - + PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength, - PinWidget::MAXIMUM_LABEL_WIDTH, - PinWidget::MAXIMUM_LABEL_HEIGHT - ), - Qt::AlignCenter, - pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText - ); + + PinWidget::MAXIMUM_LABEL_HEIGHT, + pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2), + pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength + + PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength + )); + + painter.setPen(pinDirectionFontColor); + painter.drawText( + QRect( + pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2) + - (PinWidget::MAXIMUM_LABEL_WIDTH / 2), + pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength + + PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength, + PinWidget::MAXIMUM_LABEL_WIDTH, + PinWidget::MAXIMUM_LABEL_HEIGHT + ), + Qt::AlignCenter, + pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText + ); + } } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidget.cpp index 85b1347b..66f74950 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidget.cpp @@ -4,90 +4,90 @@ #include "src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.hpp" -using namespace Bloom; -using namespace Bloom::Widgets::InsightTargetWidgets; +namespace Bloom::Widgets::InsightTargetWidgets +{ + using Bloom::Targets::TargetState; -using Bloom::Targets::TargetState; + TargetPackageWidget::TargetPackageWidget( + Targets::TargetVariant targetVariant, + InsightWorker& insightWorker, + QWidget* parent + ): QWidget(parent), targetVariant(std::move(targetVariant)), insightWorker(insightWorker) { + QObject::connect( + &(this->insightWorker), + &InsightWorker::targetStateUpdated, + this, + &TargetPackageWidget::onTargetStateChanged + ); -TargetPackageWidget::TargetPackageWidget( - Targets::TargetVariant targetVariant, - InsightWorker& insightWorker, - QWidget* parent -): QWidget(parent), targetVariant(std::move(targetVariant)), insightWorker(insightWorker) { - QObject::connect( - &(this->insightWorker), - &InsightWorker::targetStateUpdated, - this, - &TargetPackageWidget::onTargetStateChanged - ); + QObject::connect( + &(this->insightWorker), + &InsightWorker::targetRegistersWritten, + this, + &TargetPackageWidget::onRegistersWritten + ); - QObject::connect( - &(this->insightWorker), - &InsightWorker::targetRegistersWritten, - this, - &TargetPackageWidget::onRegistersWritten - ); + this->setDisabled(true); + } - this->setDisabled(true); -} - -void TargetPackageWidget::refreshPinStates(std::optional> callback) { - auto* refreshTask = new RefreshTargetPinStates(this->targetVariant.id); - QObject::connect( - refreshTask, - &RefreshTargetPinStates::targetPinStatesRetrieved, - this, - &TargetPackageWidget::updatePinStates - ); - - if (callback.has_value()) { + void TargetPackageWidget::refreshPinStates(std::optional> callback) { + auto* refreshTask = new RefreshTargetPinStates(this->targetVariant.id); QObject::connect( refreshTask, - &InsightWorkerTask::completed, + &RefreshTargetPinStates::targetPinStatesRetrieved, this, - callback.value() + &TargetPackageWidget::updatePinStates ); + + if (callback.has_value()) { + QObject::connect( + refreshTask, + &InsightWorkerTask::completed, + this, + callback.value() + ); + } + + this->insightWorker.queueTask(refreshTask); } - this->insightWorker.queueTask(refreshTask); -} + void TargetPackageWidget::updatePinStates(const Targets::TargetPinStateMappingType& pinStatesByNumber) { + for (auto& pinWidget : this->pinWidgets) { + auto pinNumber = pinWidget->getPinNumber(); + if (pinStatesByNumber.contains(pinNumber)) { + pinWidget->updatePinState(pinStatesByNumber.at(pinNumber)); + } + } -void TargetPackageWidget::updatePinStates(const Targets::TargetPinStateMappingType& pinStatesByNumber) { - for (auto& pinWidget : this->pinWidgets) { - auto pinNumber = pinWidget->getPinNumber(); - if (pinStatesByNumber.contains(pinNumber)) { - pinWidget->updatePinState(pinStatesByNumber.at(pinNumber)); + this->update(); + } + + void TargetPackageWidget::onTargetStateChanged(TargetState newState) { + this->targetState = newState; + + if (newState == TargetState::RUNNING) { + this->setDisabled(true); + + } else if (newState == TargetState::STOPPED) { + this->refreshPinStates([this] { + if (this->targetState == TargetState::STOPPED) { + this->setDisabled(false); + } + }); } } - this->update(); -} - -void TargetPackageWidget::onTargetStateChanged(TargetState newState) { - this->targetState = newState; - - if (newState == TargetState::RUNNING) { - this->setDisabled(true); - - } else if (newState == TargetState::STOPPED) { - this->refreshPinStates([this] { - if (this->targetState == TargetState::STOPPED) { - this->setDisabled(false); - } - }); - } -} - -void TargetPackageWidget::onRegistersWritten(Targets::TargetRegisters targetRegisters) { - if (this->targetState != TargetState::STOPPED) { - return; - } - - // If a PORT register was just updated, refresh pin states. - for (const auto& targetRegister : targetRegisters) { - if (targetRegister.descriptor.type == Targets::TargetRegisterType::PORT_REGISTER) { - this->refreshPinStates(); + void TargetPackageWidget::onRegistersWritten(Targets::TargetRegisters targetRegisters) { + if (this->targetState != TargetState::STOPPED) { return; } + + // If a PORT register was just updated, refresh pin states. + for (const auto& targetRegister : targetRegisters) { + if (targetRegister.descriptor.type == Targets::TargetRegisterType::PORT_REGISTER) { + this->refreshPinStates(); + return; + } + } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidgetContainer.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidgetContainer.cpp index c2fe6495..311ecd97 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidgetContainer.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidgetContainer.cpp @@ -2,29 +2,29 @@ #include -using namespace Bloom; -using namespace Bloom::Widgets::InsightTargetWidgets; +namespace Bloom::Widgets::InsightTargetWidgets +{ + TargetPackageWidgetContainer::TargetPackageWidgetContainer(QWidget* parent): QWidget(parent) {} -TargetPackageWidgetContainer::TargetPackageWidgetContainer(QWidget* parent): QWidget(parent) {} + void TargetPackageWidgetContainer::setPackageWidget(TargetPackageWidget* packageWidget) { + this->packageWidget = packageWidget; -void TargetPackageWidgetContainer::setPackageWidget(TargetPackageWidget* packageWidget) { - this->packageWidget = packageWidget; - - if (packageWidget != nullptr) { - this->layout()->addWidget(packageWidget); - } -} - -void TargetPackageWidgetContainer::resizeEvent(QResizeEvent* event) { - if (this->packageWidget == nullptr) { - return; + if (packageWidget != nullptr) { + this->layout()->addWidget(packageWidget); + } } - const auto packageSize = this->packageWidget->size(); - this->packageWidget->setGeometry( - (this->width() / 2) - (packageSize.width() / 2), - (this->height() / 2) - (packageSize.height() / 2), - packageSize.width(), - packageSize.height() - ); + void TargetPackageWidgetContainer::resizeEvent(QResizeEvent* event) { + if (this->packageWidget == nullptr) { + return; + } + + const auto packageSize = this->packageWidget->size(); + this->packageWidget->setGeometry( + (this->width() / 2) - (packageSize.width() / 2), + (this->height() / 2) - (packageSize.height() / 2), + packageSize.width(), + packageSize.height() + ); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinBodyWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinBodyWidget.cpp index 22403cb1..65c4411e 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinBodyWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinBodyWidget.cpp @@ -2,72 +2,74 @@ #include "TargetPinBodyWidget.hpp" -using namespace Bloom::Widgets::InsightTargetWidgets; -using namespace Bloom::Targets; +namespace Bloom::Widgets::InsightTargetWidgets +{ + using namespace Bloom::Targets; -TargetPinBodyWidget::TargetPinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor) -: QWidget(parent), pinDescriptor(std::move(pinDescriptor)) { - this->setObjectName("target-pin-body"); - this->setToolTip(QString::fromStdString(this->pinDescriptor.name).toUpper()); -} + TargetPinBodyWidget::TargetPinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor) + : QWidget(parent), pinDescriptor(std::move(pinDescriptor)) { + this->setObjectName("target-pin-body"); + this->setToolTip(QString::fromStdString(this->pinDescriptor.name).toUpper()); + } -QColor TargetPinBodyWidget::getBodyColor() { - auto pinColor = this->defaultBodyColor; + QColor TargetPinBodyWidget::getBodyColor() { + auto pinColor = this->defaultBodyColor; - if (this->pinDescriptor.type == TargetPinType::VCC) { - pinColor = this->vccBodyColor; + if (this->pinDescriptor.type == TargetPinType::VCC) { + pinColor = this->vccBodyColor; - } else if (this->pinDescriptor.type == TargetPinType::GND) { - pinColor = this->gndBodyColor; + } else if (this->pinDescriptor.type == TargetPinType::GND) { + pinColor = this->gndBodyColor; - } else if (this->pinDescriptor.type == TargetPinType::GPIO) { - if (this->pinState.has_value() - && this->pinState->ioState.has_value() - && this->pinState->ioDirection.has_value() - ) { - const auto ioDirection = this->pinState->ioDirection.value(); - const auto ioState = this->pinState->ioState.value(); - - if (this->pinState->ioState.value() == TargetPinState::IoState::HIGH) { - pinColor = ioDirection == TargetPinState::IoDirection::OUTPUT ? - this->outputHighBodyColor : this->inputHighBodyColor; - } - - if (( - ioDirection == TargetPinState::IoDirection::OUTPUT - || (ioDirection == TargetPinState::IoDirection::INPUT && ioState == TargetPinState::IoState::LOW) - ) && !this->hoverActive + } else if (this->pinDescriptor.type == TargetPinType::GPIO) { + if (this->pinState.has_value() + && this->pinState->ioState.has_value() + && this->pinState->ioDirection.has_value() ) { - pinColor.setAlpha(220); + const auto ioDirection = this->pinState->ioDirection.value(); + const auto ioState = this->pinState->ioState.value(); + + if (this->pinState->ioState.value() == TargetPinState::IoState::HIGH) { + pinColor = ioDirection == TargetPinState::IoDirection::OUTPUT ? + this->outputHighBodyColor : this->inputHighBodyColor; + } + + if (( + ioDirection == TargetPinState::IoDirection::OUTPUT + || (ioDirection == TargetPinState::IoDirection::INPUT && ioState == TargetPinState::IoState::LOW) + ) && !this->hoverActive + ) { + pinColor.setAlpha(220); + } } } + + if (!this->isEnabled()) { + pinColor.setAlpha(this->disableAlphaLevel); + } + + return pinColor; } - if (!this->isEnabled()) { - pinColor.setAlpha(this->disableAlphaLevel); - } - - return pinColor; -} - -bool TargetPinBodyWidget::event(QEvent* event) { - if (this->pinState.has_value() && this->pinState->ioDirection == TargetPinState::IoDirection::OUTPUT) { - switch (event->type()) { - case QEvent::Enter: { - this->hoverActive = true; - this->repaint(); - break; - } - case QEvent::Leave: { - this->hoverActive = false; - this->repaint(); - break; - } - default: { - break; + bool TargetPinBodyWidget::event(QEvent* event) { + if (this->pinState.has_value() && this->pinState->ioDirection == TargetPinState::IoDirection::OUTPUT) { + switch (event->type()) { + case QEvent::Enter: { + this->hoverActive = true; + this->repaint(); + break; + } + case QEvent::Leave: { + this->hoverActive = false; + this->repaint(); + break; + } + default: { + break; + } } } - } - return QWidget::event(event); + return QWidget::event(event); + } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.cpp index 339db072..4ca9d8e5 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.cpp @@ -2,49 +2,51 @@ #include "src/Insight/InsightWorker/Tasks/SetTargetPinState.hpp" -using namespace Bloom; -using namespace Bloom::Widgets::InsightTargetWidgets; +namespace Bloom::Widgets::InsightTargetWidgets +{ + using Bloom::Targets::TargetVariant; + using Bloom::Targets::TargetPinDescriptor; + using Bloom::Targets::TargetPinType; + using Bloom::Targets::TargetPinState; -using Bloom::Targets::TargetVariant; -using Bloom::Targets::TargetPinDescriptor; -using Bloom::Targets::TargetPinType; -using Bloom::Targets::TargetPinState; - -TargetPinWidget::TargetPinWidget( - Targets::TargetPinDescriptor pinDescriptor, - Targets::TargetVariant targetVariant, - InsightWorker& insightWorker, - QWidget* parent -): QWidget(parent), -insightWorker(insightWorker), -targetVariant(std::move(targetVariant)), -pinDescriptor(std::move(pinDescriptor)) { - if (this->pinDescriptor.type == TargetPinType::UNKNOWN) { - this->setDisabled(true); - } -} - -void TargetPinWidget::onWidgetBodyClicked() { - // Currently, we only allow users to toggle the IO state of output pins - if (this->pinState.has_value() - && this->pinState.value().ioDirection == TargetPinState::IoDirection::OUTPUT + TargetPinWidget::TargetPinWidget( + Targets::TargetPinDescriptor pinDescriptor, + Targets::TargetVariant targetVariant, + InsightWorker& insightWorker, + QWidget* parent + ) + : QWidget(parent) + , insightWorker(insightWorker) + , targetVariant(std::move(targetVariant)) + , pinDescriptor(std::move(pinDescriptor) ) { - this->setDisabled(true); + if (this->pinDescriptor.type == TargetPinType::UNKNOWN) { + this->setDisabled(true); + } + } - auto pinState = this->pinState.value(); - pinState.ioState = (pinState.ioState == TargetPinState::IoState::HIGH) ? - TargetPinState::IoState::LOW : TargetPinState::IoState::HIGH; + void TargetPinWidget::onWidgetBodyClicked() { + // Currently, we only allow users to toggle the IO state of output pins + if (this->pinState.has_value() + && this->pinState.value().ioDirection == TargetPinState::IoDirection::OUTPUT + ) { + this->setDisabled(true); - auto* setPinStateTask = new SetTargetPinState(this->pinDescriptor, pinState); - QObject::connect(setPinStateTask, &InsightWorkerTask::completed, this, [this, pinState] { - this->updatePinState(pinState); - this->setDisabled(false); - }); + auto pinState = this->pinState.value(); + pinState.ioState = (pinState.ioState == TargetPinState::IoState::HIGH) ? + TargetPinState::IoState::LOW : TargetPinState::IoState::HIGH; - QObject::connect(setPinStateTask, &InsightWorkerTask::failed, this, [this] { - this->setDisabled(false); - }); + auto* setPinStateTask = new SetTargetPinState(this->pinDescriptor, pinState); + QObject::connect(setPinStateTask, &InsightWorkerTask::completed, this, [this, pinState] { + this->updatePinState(pinState); + this->setDisabled(false); + }); - this->insightWorker.queueTask(setPinStateTask); + QObject::connect(setPinStateTask, &InsightWorkerTask::failed, this, [this] { + this->setDisabled(false); + }); + + this->insightWorker.queueTask(setPinStateTask); + } } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TextInput.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TextInput.cpp index 8cb20fdd..6c16086b 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TextInput.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TextInput.cpp @@ -3,29 +3,30 @@ #include #include -using namespace Bloom::Widgets; +namespace Bloom::Widgets +{ + TextInput::TextInput(QWidget* parent): QLineEdit(parent) {} -TextInput::TextInput(QWidget* parent): QLineEdit(parent) {} + void TextInput::contextMenuEvent(QContextMenuEvent* event) { + if (QMenu* menu = this->createStandardContextMenu()) { + menu->setAttribute(Qt::WA_DeleteOnClose); -void TextInput::contextMenuEvent(QContextMenuEvent* event) { - if (QMenu* menu = this->createStandardContextMenu()) { - menu->setAttribute(Qt::WA_DeleteOnClose); + // Remove default icons + for (auto& action : menu->actions()) { + action->setIcon(QIcon()); + } - // Remove default icons - for (auto& action : menu->actions()) { - action->setIcon(QIcon()); + menu->popup(event->globalPos()); } + } - menu->popup(event->globalPos()); + void TextInput::focusInEvent(QFocusEvent* event) { + QLineEdit::focusInEvent(event); + emit this->focusChanged(); + } + + void TextInput::focusOutEvent(QFocusEvent* event) { + QLineEdit::focusOutEvent(event); + emit this->focusChanged(); } } - -void TextInput::focusInEvent(QFocusEvent* event) { - QLineEdit::focusInEvent(event); - emit this->focusChanged(); -} - -void TextInput::focusOutEvent(QFocusEvent* event) { - QLineEdit::focusOutEvent(event); - emit this->focusChanged(); -} diff --git a/src/Logger/Logger.cpp b/src/Logger/Logger.cpp index 275404dc..a51333c2 100644 --- a/src/Logger/Logger.cpp +++ b/src/Logger/Logger.cpp @@ -2,65 +2,70 @@ #include -using namespace Bloom; +namespace Bloom +{ + void Logger::configure(ProjectConfig& projectConfig) { + if (projectConfig.debugLoggingEnabled) { + Logger::debugPrintingEnabled = true; + Logger::debug("Debug log printing has been enabled."); + } + } -void Logger::configure(ProjectConfig& projectConfig) { - if (projectConfig.debugLoggingEnabled) { - Logger::debugPrintingEnabled = true; - Logger::debug("Debug log printing has been enabled."); - } -} - -void Logger::silence() { - Logger::debugPrintingEnabled = false; - Logger::infoPrintingEnabled = false; - Logger::errorPrintingEnabled = false; - Logger::warningPrintingEnabled = false; -} - -void Logger::log(const std::string& message, LogLevel logLevel, bool print) { - auto lock = std::unique_lock(Logger::logMutex); - auto logEntry = LogEntry(message, logLevel); - Logger::logEntries.push_back(logEntry); - auto index = Logger::logEntries.size(); - static auto timezoneAbbreviation = DateTime::getTimeZoneAbbreviation(logEntry.timestamp).toStdString(); - - if (print) { - // Print the timestamp and index in a green font color: - std::cout << "\033[32m"; - std::cout << logEntry.timestamp.toString("yyyy-MM-dd hh:mm:ss ").toStdString() + timezoneAbbreviation; - - if (!logEntry.threadName.empty()) { - std::cout << " [" << logEntry.threadName << "]"; - } - - // The index serves as an ID for each log entry. Just here for convenience when referencing specific log entries. - std::cout << " [" << index << "]: "; - std::cout << "\033[0m"; - - switch (logLevel) { - case LogLevel::ERROR: { - // Errors in red - std::cout << "\033[31m"; - std::cout << "[ERROR] "; - break; - } - case LogLevel::WARNING: { - // Warnings in yellow - std::cout << "\033[33m"; - std::cout << "[WARNING] "; - break; - } - case LogLevel::INFO: { - std::cout << "[INFO] "; - break; - } - case LogLevel::DEBUG: { - std::cout << "[DEBUG] "; - break; - } - } - - std::cout << logEntry.message << "\033[0m" << std::endl; + void Logger::silence() { + Logger::debugPrintingEnabled = false; + Logger::infoPrintingEnabled = false; + Logger::errorPrintingEnabled = false; + Logger::warningPrintingEnabled = false; + } + + void Logger::log(const std::string& message, LogLevel logLevel, bool print) { + auto lock = std::unique_lock(Logger::logMutex); + auto logEntry = LogEntry(message, logLevel); + Logger::logEntries.push_back(logEntry); + auto index = Logger::logEntries.size(); + static auto timezoneAbbreviation = DateTime::getTimeZoneAbbreviation(logEntry.timestamp).toStdString(); + + if (print) { + // Print the timestamp and index in a green font color: + std::cout << "\033[32m"; + std::cout << logEntry.timestamp.toString("yyyy-MM-dd hh:mm:ss ").toStdString() + + timezoneAbbreviation; + + if (!logEntry.threadName.empty()) { + std::cout << " [" << logEntry.threadName << "]"; + } + + /* + * The index serves as an ID for each log entry. Just here for convenience when referencing specific + * log entries. + */ + std::cout << " [" << index << "]: "; + std::cout << "\033[0m"; + + switch (logLevel) { + case LogLevel::ERROR: { + // Errors in red + std::cout << "\033[31m"; + std::cout << "[ERROR] "; + break; + } + case LogLevel::WARNING: { + // Warnings in yellow + std::cout << "\033[33m"; + std::cout << "[WARNING] "; + break; + } + case LogLevel::INFO: { + std::cout << "[INFO] "; + break; + } + case LogLevel::DEBUG: { + std::cout << "[DEBUG] "; + break; + } + } + + std::cout << logEntry.message << "\033[0m" << std::endl; + } } } diff --git a/src/ProjectConfig.cpp b/src/ProjectConfig.cpp index 14608546..128d9052 100644 --- a/src/ProjectConfig.cpp +++ b/src/ProjectConfig.cpp @@ -3,105 +3,106 @@ #include "src/Logger/Logger.hpp" #include "src/Exceptions/InvalidConfig.hpp" -using namespace Bloom; - -ProjectConfig::ProjectConfig(const QJsonObject& jsonObject) { - if (!jsonObject.contains("environments")) { - throw Exceptions::InvalidConfig( - "No environments found - please review the bloom.json configuration file and ensure that " - "no syntax errors are present." - ); - } - - // Extract all environment objects from JSON config. - auto environments = jsonObject.find("environments").value().toObject(); - for (auto environmentIt = environments.begin(); environmentIt != environments.end(); environmentIt++) { - auto environmentName = environmentIt.key().toStdString(); - - try { - this->environments.insert( - std::pair( - environmentName, - EnvironmentConfig(environmentName, environmentIt.value().toObject()) - ) +namespace Bloom +{ + ProjectConfig::ProjectConfig(const QJsonObject& jsonObject) { + if (!jsonObject.contains("environments")) { + throw Exceptions::InvalidConfig( + "No environments found - please review the bloom.json configuration file and ensure that " + "no syntax errors are present." ); + } - } catch (Exceptions::InvalidConfig& exception) { - Logger::error("Invalid environment config for environment '" + environmentName + "': " - + exception.getMessage() + " Environment will be ignored."); + // Extract all environment objects from JSON config. + auto environments = jsonObject.find("environments").value().toObject(); + for (auto environmentIt = environments.begin(); environmentIt != environments.end(); environmentIt++) { + auto environmentName = environmentIt.key().toStdString(); + + try { + this->environments.insert( + std::pair( + environmentName, + EnvironmentConfig(environmentName, environmentIt.value().toObject()) + ) + ); + + } catch (Exceptions::InvalidConfig& exception) { + Logger::error("Invalid environment config for environment '" + environmentName + "': " + + exception.getMessage() + " Environment will be ignored."); + } + } + + if (jsonObject.contains("debugServer")) { + this->debugServerConfig = DebugServerConfig(jsonObject.find("debugServer")->toObject()); + } + + if (jsonObject.contains("insight")) { + this->insightConfig = InsightConfig(jsonObject.find("insight")->toObject()); + } + + if (jsonObject.contains("debugLoggingEnabled")) { + this->debugLoggingEnabled = jsonObject.find("debugLoggingEnabled").value().toBool(); } } - if (jsonObject.contains("debugServer")) { - this->debugServerConfig = DebugServerConfig(jsonObject.find("debugServer")->toObject()); + InsightConfig::InsightConfig(const QJsonObject& jsonObject) { + if (jsonObject.contains("enabled")) { + this->insightEnabled = jsonObject.find("enabled").value().toBool(); + } } - if (jsonObject.contains("insight")) { - this->insightConfig = InsightConfig(jsonObject.find("insight")->toObject()); + EnvironmentConfig::EnvironmentConfig(std::string name, QJsonObject jsonObject) { + if (!jsonObject.contains("debugTool")) { + throw Exceptions::InvalidConfig("No debug tool configuration provided."); + } + + if (!jsonObject.contains("target")) { + throw Exceptions::InvalidConfig("No target configuration provided."); + } + + this->name = std::move(name); + this->debugToolConfig = DebugToolConfig(jsonObject.find("debugTool")->toObject()); + this->targetConfig = TargetConfig(jsonObject.find("target")->toObject()); + + if (jsonObject.contains("debugServer")) { + this->debugServerConfig = DebugServerConfig(jsonObject.find("debugServer")->toObject()); + } + + if (jsonObject.contains("insight")) { + this->insightConfig = InsightConfig(jsonObject.find("insight")->toObject()); + } } - if (jsonObject.contains("debugLoggingEnabled")) { - this->debugLoggingEnabled = jsonObject.find("debugLoggingEnabled").value().toBool(); + TargetConfig::TargetConfig(const QJsonObject& jsonObject) { + if (!jsonObject.contains("name")) { + throw Exceptions::InvalidConfig("No target name found."); + } + + this->name = jsonObject.find("name")->toString().toLower().toStdString(); + + if (jsonObject.contains("variantName")) { + this->variantName = jsonObject.find("variantName").value().toString().toLower().toStdString(); + } + + this->jsonObject = jsonObject; + } + + DebugToolConfig::DebugToolConfig(const QJsonObject& jsonObject) { + if (!jsonObject.contains("name")) { + throw Exceptions::InvalidConfig("No debug tool name found."); + } + + this->name = jsonObject.find("name")->toString().toLower().toStdString(); + + if (jsonObject.contains("releasePostDebugSession")) { + this->releasePostDebugSession = jsonObject.find("releasePostDebugSession").value().toBool(); + } + + this->jsonObject = jsonObject; + } + + DebugServerConfig::DebugServerConfig(const QJsonObject& jsonObject) { + this->name = jsonObject.find("name")->toString().toLower().toStdString(); + this->jsonObject = jsonObject; } } - -InsightConfig::InsightConfig(const QJsonObject& jsonObject) { - if (jsonObject.contains("enabled")) { - this->insightEnabled = jsonObject.find("enabled").value().toBool(); - } -} - -EnvironmentConfig::EnvironmentConfig(std::string name, QJsonObject jsonObject) { - if (!jsonObject.contains("debugTool")) { - throw Exceptions::InvalidConfig("No debug tool configuration provided."); - } - - if (!jsonObject.contains("target")) { - throw Exceptions::InvalidConfig("No target configuration provided."); - } - - this->name = std::move(name); - this->debugToolConfig = DebugToolConfig(jsonObject.find("debugTool")->toObject()); - this->targetConfig = TargetConfig(jsonObject.find("target")->toObject()); - - if (jsonObject.contains("debugServer")) { - this->debugServerConfig = DebugServerConfig(jsonObject.find("debugServer")->toObject()); - } - - if (jsonObject.contains("insight")) { - this->insightConfig = InsightConfig(jsonObject.find("insight")->toObject()); - } -} - -TargetConfig::TargetConfig(const QJsonObject& jsonObject) { - if (!jsonObject.contains("name")) { - throw Exceptions::InvalidConfig("No target name found."); - } - - this->name = jsonObject.find("name")->toString().toLower().toStdString(); - - if (jsonObject.contains("variantName")) { - this->variantName = jsonObject.find("variantName").value().toString().toLower().toStdString(); - } - - this->jsonObject = jsonObject; -} - -DebugToolConfig::DebugToolConfig(const QJsonObject& jsonObject) { - if (!jsonObject.contains("name")) { - throw Exceptions::InvalidConfig("No debug tool name found."); - } - - this->name = jsonObject.find("name")->toString().toLower().toStdString(); - - if (jsonObject.contains("releasePostDebugSession")) { - this->releasePostDebugSession = jsonObject.find("releasePostDebugSession").value().toBool(); - } - - this->jsonObject = jsonObject; -} - -DebugServerConfig::DebugServerConfig(const QJsonObject& jsonObject) { - this->name = jsonObject.find("name")->toString().toLower().toStdString(); - this->jsonObject = jsonObject; -} diff --git a/src/ProjectSettings.cpp b/src/ProjectSettings.cpp index 5ce06638..c53b1941 100644 --- a/src/ProjectSettings.cpp +++ b/src/ProjectSettings.cpp @@ -2,272 +2,279 @@ #include -#include "src/Logger/Logger.hpp" - -using namespace Bloom; - -ProjectSettings::ProjectSettings(const QJsonObject& jsonObject) { - if (jsonObject.contains("insight")) { - this->insightSettings = InsightProjectSettings(jsonObject.find("insight")->toObject()); +namespace Bloom +{ + ProjectSettings::ProjectSettings(const QJsonObject& jsonObject) { + if (jsonObject.contains("insight")) { + this->insightSettings = InsightProjectSettings(jsonObject.find("insight")->toObject()); + } } -} -QJsonObject ProjectSettings::toJson() const { - auto projectSettingsObj = QJsonObject(); + QJsonObject ProjectSettings::toJson() const { + auto projectSettingsObj = QJsonObject(); - projectSettingsObj.insert("insight", this->insightSettings.toJson()); + projectSettingsObj.insert("insight", this->insightSettings.toJson()); - return projectSettingsObj; -} + return projectSettingsObj; + } -InsightProjectSettings::InsightProjectSettings(const QJsonObject& jsonObject) { - if (jsonObject.contains("mainWindowSize")) { - const auto mainWindowSizeObj = jsonObject.find("mainWindowSize")->toObject(); + InsightProjectSettings::InsightProjectSettings(const QJsonObject& jsonObject) { + if (jsonObject.contains("mainWindowSize")) { + const auto mainWindowSizeObj = jsonObject.find("mainWindowSize")->toObject(); - if (mainWindowSizeObj.contains("width") && mainWindowSizeObj.contains("height")) { - this->mainWindowSize = QSize( - mainWindowSizeObj.find("width")->toInt(), - mainWindowSizeObj.find("height")->toInt() + if (mainWindowSizeObj.contains("width") && mainWindowSizeObj.contains("height")) { + this->mainWindowSize = QSize( + mainWindowSizeObj.find("width")->toInt(), + mainWindowSizeObj.find("height")->toInt() + ); + } + } + + if (jsonObject.contains("memoryInspectionPaneSettings")) { + const auto settingsMappingObj = jsonObject.find("memoryInspectionPaneSettings")->toObject(); + + for (auto settingsIt = settingsMappingObj.begin(); settingsIt != settingsMappingObj.end(); settingsIt++) { + const auto settingsObj = settingsIt.value().toObject(); + const auto memoryTypeName = settingsIt.key(); + + if (!InsightProjectSettings::memoryTypesByName.contains(memoryTypeName)) { + continue; + } + + const auto memoryType = InsightProjectSettings::memoryTypesByName.at(memoryTypeName); + auto inspectionPaneSettings = Widgets::TargetMemoryInspectionPaneSettings(); + + if (settingsObj.contains("hexViewerSettings")) { + auto& hexViewerSettings = inspectionPaneSettings.hexViewerWidgetSettings; + const auto hexViewerSettingsObj = settingsObj.find("hexViewerSettings")->toObject(); + + if (hexViewerSettingsObj.contains("highlightStackMemory")) { + hexViewerSettings.highlightStackMemory = + hexViewerSettingsObj.value("highlightStackMemory").toBool(); + } + + if (hexViewerSettingsObj.contains("highlightFocusedMemory")) { + hexViewerSettings.highlightFocusedMemory = + hexViewerSettingsObj.value("highlightFocusedMemory").toBool(); + } + + if (hexViewerSettingsObj.contains("highlightHoveredRowAndCol")) { + hexViewerSettings.highlightHoveredRowAndCol = + hexViewerSettingsObj.value("highlightHoveredRowAndCol").toBool(); + } + + if (hexViewerSettingsObj.contains("displayAsciiValues")) { + hexViewerSettings.displayAsciiValues = + hexViewerSettingsObj.value("displayAsciiValues").toBool(); + } + + if (hexViewerSettingsObj.contains("displayAnnotations")) { + hexViewerSettings.displayAnnotations = + hexViewerSettingsObj.value("displayAnnotations").toBool(); + } + } + + if (settingsObj.contains("focusedRegions")) { + const auto focusedRegions = settingsObj.find("focusedRegions")->toArray(); + + for (const auto& regionValue : focusedRegions) { + const auto regionObj = regionValue.toObject(); + + if (!regionObj.contains("name") + || !regionObj.contains("addressRange") + || !regionObj.contains("addressInputType") + || !regionObj.contains("createdTimestamp") + || !regionObj.contains("dataType") + ) { + continue; + } + + const auto addressRangeObj = regionObj.find("addressRange")->toObject(); + if (!addressRangeObj.contains("startAddress") + || !addressRangeObj.contains("endAddress") + ) { + continue; + } + + auto region = FocusedMemoryRegion( + regionObj.find("name")->toString(), + { + static_cast(addressRangeObj.find("startAddress") + ->toInteger()), + static_cast(addressRangeObj.find("endAddress") + ->toInteger()), + } + ); + + region.createdDate.setSecsSinceEpoch(regionObj.find("createdTimestamp")->toInteger()); + + const auto addressInputType = InsightProjectSettings::addressRangeInputTypesByName.valueAt( + regionObj.find("addressInputType")->toString() + ); + + if (addressInputType.has_value()) { + region.addressRangeInputType = addressInputType.value(); + } + + const auto dataType = InsightProjectSettings::regionDataTypesByName.valueAt( + regionObj.find("dataType")->toString() + ); + + if (dataType.has_value()) { + region.dataType = dataType.value(); + } + + const auto endianness = InsightProjectSettings::regionEndiannessByName.valueAt( + regionObj.find("endianness")->toString() + ); + + if (endianness.has_value()) { + region.endianness = endianness.value(); + } + + inspectionPaneSettings.focusedMemoryRegions.emplace_back(region); + } + } + + if (settingsObj.contains("excludedRegions")) { + const auto excludedRegions = settingsObj.find("excludedRegions")->toArray(); + + for (const auto& regionValue : excludedRegions) { + const auto regionObj = regionValue.toObject(); + + if (!regionObj.contains("name") + || !regionObj.contains("addressRange") + || !regionObj.contains("addressInputType") + || !regionObj.contains("createdTimestamp") + ) { + continue; + } + + const auto addressRangeObj = regionObj.find("addressRange")->toObject(); + if (!addressRangeObj.contains("startAddress") + || !addressRangeObj.contains("endAddress") + ) { + continue; + } + + auto region = ExcludedMemoryRegion( + regionObj.find("name")->toString(), + { + static_cast(addressRangeObj.find("startAddress") + ->toInteger()), + static_cast(addressRangeObj.find("endAddress") + ->toInteger()), + } + ); + + region.createdDate.setSecsSinceEpoch(regionObj.find("createdTimestamp")->toInteger()); + + const auto addressInputType = InsightProjectSettings::addressRangeInputTypesByName.valueAt( + regionObj.find("addressInputType")->toString() + ); + + if (addressInputType.has_value()) { + region.addressRangeInputType = addressInputType.value(); + } + + inspectionPaneSettings.excludedMemoryRegions.emplace_back(region); + } + } + + this->memoryInspectionPaneSettingsByMemoryType.insert(std::pair(memoryType, inspectionPaneSettings)); + } + } + } + + QJsonObject InsightProjectSettings::toJson() const { + auto insightObj = QJsonObject(); + + if (this->mainWindowSize.has_value()) { + insightObj.insert("mainWindowSize", QJsonObject({ + {"width", this->mainWindowSize->width()}, + {"height", this->mainWindowSize->height()}, + })); + } + + auto memoryInspectionPaneSettingsObj = QJsonObject(); + + for (const auto& [memoryType, inspectionPaneSettings] : this->memoryInspectionPaneSettingsByMemoryType) { + if (!InsightProjectSettings::memoryTypesByName.contains(memoryType)) { + // This is just a precaution - all known memory types should be in the mapping. + continue; + } + + auto settingsObj = QJsonObject(); + + const auto& hexViewerSettings = inspectionPaneSettings.hexViewerWidgetSettings; + settingsObj.insert("hexViewerSettings", QJsonObject({ + {"highlightStackMemory", hexViewerSettings.highlightStackMemory}, + {"highlightFocusedMemory", hexViewerSettings.highlightFocusedMemory}, + {"highlightHoveredRowAndCol", hexViewerSettings.highlightHoveredRowAndCol}, + {"displayAsciiValues", hexViewerSettings.displayAsciiValues}, + {"displayAnnotations", hexViewerSettings.displayAnnotations}, + })); + + const auto& regionDataTypesByName = InsightProjectSettings::regionDataTypesByName; + const auto& regionEndiannessByName = InsightProjectSettings::regionEndiannessByName; + const auto& addressRangeInputTypesByName = InsightProjectSettings::addressRangeInputTypesByName; + + auto focusedRegions = QJsonArray(); + for (const auto& focusedRegion : inspectionPaneSettings.focusedMemoryRegions) { + if (!regionDataTypesByName.contains(focusedRegion.dataType) + || !regionEndiannessByName.contains(focusedRegion.endianness) + || !addressRangeInputTypesByName.contains(focusedRegion.addressRangeInputType) + ) { + continue; + } + + const auto addressRangeObj = QJsonObject({ + {"startAddress", static_cast(focusedRegion.addressRange.startAddress)}, + {"endAddress", static_cast(focusedRegion.addressRange.endAddress)}, + }); + + auto regionObj = QJsonObject({ + {"name", focusedRegion.name}, + {"addressRange", addressRangeObj}, + {"createdTimestamp", focusedRegion.createdDate.toSecsSinceEpoch()}, + {"addressInputType", addressRangeInputTypesByName.at(focusedRegion.addressRangeInputType)}, + {"dataType", regionDataTypesByName.at(focusedRegion.dataType)}, + {"endianness", regionEndiannessByName.at(focusedRegion.endianness)}, + }); + + focusedRegions.push_back(regionObj); + } + + auto excludedRegions = QJsonArray(); + for (const auto& excludedRegion : inspectionPaneSettings.excludedMemoryRegions) { + if (!addressRangeInputTypesByName.contains(excludedRegion.addressRangeInputType)) { + continue; + } + + const auto addressRangeObj = QJsonObject({ + {"startAddress", static_cast(excludedRegion.addressRange.startAddress)}, + {"endAddress", static_cast(excludedRegion.addressRange.endAddress)}, + }); + + auto regionObj = QJsonObject({ + {"name", excludedRegion.name}, + {"addressRange", addressRangeObj}, + {"createdTimestamp", excludedRegion.createdDate.toSecsSinceEpoch()}, + {"addressInputType", addressRangeInputTypesByName.at(excludedRegion.addressRangeInputType)}, + }); + + excludedRegions.push_back(regionObj); + } + + settingsObj.insert("focusedRegions", focusedRegions); + settingsObj.insert("excludedRegions", excludedRegions); + memoryInspectionPaneSettingsObj.insert( + InsightProjectSettings::memoryTypesByName.at(memoryType), + settingsObj ); } - } - if (jsonObject.contains("memoryInspectionPaneSettings")) { - const auto settingsMappingObj = jsonObject.find("memoryInspectionPaneSettings")->toObject(); - - for (auto settingsIt = settingsMappingObj.begin(); settingsIt != settingsMappingObj.end(); settingsIt++) { - const auto settingsObj = settingsIt.value().toObject(); - const auto memoryTypeName = settingsIt.key(); - - if (!InsightProjectSettings::memoryTypesByName.contains(memoryTypeName)) { - continue; - } - - const auto memoryType = InsightProjectSettings::memoryTypesByName.at(memoryTypeName); - auto inspectionPaneSettings = Widgets::TargetMemoryInspectionPaneSettings(); - - if (settingsObj.contains("hexViewerSettings")) { - auto& hexViewerSettings = inspectionPaneSettings.hexViewerWidgetSettings; - const auto hexViewerSettingsObj = settingsObj.find("hexViewerSettings")->toObject(); - - if (hexViewerSettingsObj.contains("highlightStackMemory")) { - hexViewerSettings.highlightStackMemory = - hexViewerSettingsObj.value("highlightStackMemory").toBool(); - } - - if (hexViewerSettingsObj.contains("highlightFocusedMemory")) { - hexViewerSettings.highlightFocusedMemory = - hexViewerSettingsObj.value("highlightFocusedMemory").toBool(); - } - - if (hexViewerSettingsObj.contains("highlightHoveredRowAndCol")) { - hexViewerSettings.highlightHoveredRowAndCol = - hexViewerSettingsObj.value("highlightHoveredRowAndCol").toBool(); - } - - if (hexViewerSettingsObj.contains("displayAsciiValues")) { - hexViewerSettings.displayAsciiValues = - hexViewerSettingsObj.value("displayAsciiValues").toBool(); - } - - if (hexViewerSettingsObj.contains("displayAnnotations")) { - hexViewerSettings.displayAnnotations = - hexViewerSettingsObj.value("displayAnnotations").toBool(); - } - } - - if (settingsObj.contains("focusedRegions")) { - const auto focusedRegions = settingsObj.find("focusedRegions")->toArray(); - - for (const auto& regionValue : focusedRegions) { - const auto regionObj = regionValue.toObject(); - - if (!regionObj.contains("name") - || !regionObj.contains("addressRange") - || !regionObj.contains("addressInputType") - || !regionObj.contains("createdTimestamp") - || !regionObj.contains("dataType") - ) { - continue; - } - - const auto addressRangeObj = regionObj.find("addressRange")->toObject(); - if (!addressRangeObj.contains("startAddress") || !addressRangeObj.contains("endAddress")) { - continue; - } - - auto region = FocusedMemoryRegion( - regionObj.find("name")->toString(), - { - static_cast(addressRangeObj.find("startAddress")->toInteger()), - static_cast(addressRangeObj.find("endAddress")->toInteger()), - } - ); - - region.createdDate.setSecsSinceEpoch(regionObj.find("createdTimestamp")->toInteger()); - - const auto addressInputType = InsightProjectSettings::addressRangeInputTypesByName.valueAt( - regionObj.find("addressInputType")->toString() - ); - - if (addressInputType.has_value()) { - region.addressRangeInputType = addressInputType.value(); - } - - const auto dataType = InsightProjectSettings::regionDataTypesByName.valueAt( - regionObj.find("dataType")->toString() - ); - - if (dataType.has_value()) { - region.dataType = dataType.value(); - } - - const auto endianness = InsightProjectSettings::regionEndiannessByName.valueAt( - regionObj.find("endianness")->toString() - ); - - if (endianness.has_value()) { - region.endianness = endianness.value(); - } - - inspectionPaneSettings.focusedMemoryRegions.emplace_back(region); - } - } - - if (settingsObj.contains("excludedRegions")) { - const auto excludedRegions = settingsObj.find("excludedRegions")->toArray(); - - for (const auto& regionValue : excludedRegions) { - const auto regionObj = regionValue.toObject(); - - if (!regionObj.contains("name") - || !regionObj.contains("addressRange") - || !regionObj.contains("addressInputType") - || !regionObj.contains("createdTimestamp") - ) { - continue; - } - - const auto addressRangeObj = regionObj.find("addressRange")->toObject(); - if (!addressRangeObj.contains("startAddress") || !addressRangeObj.contains("endAddress")) { - continue; - } - - auto region = ExcludedMemoryRegion( - regionObj.find("name")->toString(), - { - static_cast(addressRangeObj.find("startAddress")->toInteger()), - static_cast(addressRangeObj.find("endAddress")->toInteger()), - } - ); - - region.createdDate.setSecsSinceEpoch(regionObj.find("createdTimestamp")->toInteger()); - - const auto addressInputType = InsightProjectSettings::addressRangeInputTypesByName.valueAt( - regionObj.find("addressInputType")->toString() - ); - - if (addressInputType.has_value()) { - region.addressRangeInputType = addressInputType.value(); - } - - inspectionPaneSettings.excludedMemoryRegions.emplace_back(region); - } - } - - this->memoryInspectionPaneSettingsByMemoryType.insert(std::pair(memoryType, inspectionPaneSettings)); - } + insightObj.insert("memoryInspectionPaneSettings", memoryInspectionPaneSettingsObj); + return insightObj; } } - -QJsonObject InsightProjectSettings::toJson() const { - auto insightObj = QJsonObject(); - - if (this->mainWindowSize.has_value()) { - insightObj.insert("mainWindowSize", QJsonObject({ - {"width", this->mainWindowSize->width()}, - {"height", this->mainWindowSize->height()}, - })); - } - - auto memoryInspectionPaneSettingsObj = QJsonObject(); - - for (const auto& [memoryType, inspectionPaneSettings] : this->memoryInspectionPaneSettingsByMemoryType) { - if (!InsightProjectSettings::memoryTypesByName.contains(memoryType)) { - // This is just a precaution - all known memory types should be in the mapping. - continue; - } - - auto settingsObj = QJsonObject(); - - const auto& hexViewerSettings = inspectionPaneSettings.hexViewerWidgetSettings; - settingsObj.insert("hexViewerSettings", QJsonObject({ - {"highlightStackMemory", hexViewerSettings.highlightStackMemory}, - {"highlightFocusedMemory", hexViewerSettings.highlightFocusedMemory}, - {"highlightHoveredRowAndCol", hexViewerSettings.highlightHoveredRowAndCol}, - {"displayAsciiValues", hexViewerSettings.displayAsciiValues}, - {"displayAnnotations", hexViewerSettings.displayAnnotations}, - })); - - const auto& regionDataTypesByName = InsightProjectSettings::regionDataTypesByName; - const auto& regionEndiannessByName = InsightProjectSettings::regionEndiannessByName; - const auto& addressRangeInputTypesByName = InsightProjectSettings::addressRangeInputTypesByName; - - auto focusedRegions = QJsonArray(); - for (const auto& focusedRegion : inspectionPaneSettings.focusedMemoryRegions) { - if (!regionDataTypesByName.contains(focusedRegion.dataType) - || !regionEndiannessByName.contains(focusedRegion.endianness) - || !addressRangeInputTypesByName.contains(focusedRegion.addressRangeInputType) - ) { - continue; - } - - const auto addressRangeObj = QJsonObject({ - {"startAddress", static_cast(focusedRegion.addressRange.startAddress)}, - {"endAddress", static_cast(focusedRegion.addressRange.endAddress)}, - }); - - auto regionObj = QJsonObject({ - {"name", focusedRegion.name}, - {"addressRange", addressRangeObj}, - {"createdTimestamp", focusedRegion.createdDate.toSecsSinceEpoch()}, - {"addressInputType", addressRangeInputTypesByName.at(focusedRegion.addressRangeInputType)}, - {"dataType", regionDataTypesByName.at(focusedRegion.dataType)}, - {"endianness", regionEndiannessByName.at(focusedRegion.endianness)}, - }); - - focusedRegions.push_back(regionObj); - } - - auto excludedRegions = QJsonArray(); - for (const auto& excludedRegion : inspectionPaneSettings.excludedMemoryRegions) { - if (!addressRangeInputTypesByName.contains(excludedRegion.addressRangeInputType)) { - continue; - } - - const auto addressRangeObj = QJsonObject({ - {"startAddress", static_cast(excludedRegion.addressRange.startAddress)}, - {"endAddress", static_cast(excludedRegion.addressRange.endAddress)}, - }); - - auto regionObj = QJsonObject({ - {"name", excludedRegion.name}, - {"addressRange", addressRangeObj}, - {"createdTimestamp", excludedRegion.createdDate.toSecsSinceEpoch()}, - {"addressInputType", addressRangeInputTypesByName.at(excludedRegion.addressRangeInputType)}, - }); - - excludedRegions.push_back(regionObj); - } - - settingsObj.insert("focusedRegions", focusedRegions); - settingsObj.insert("excludedRegions", excludedRegions); - memoryInspectionPaneSettingsObj.insert( - InsightProjectSettings::memoryTypesByName.at(memoryType), - settingsObj - ); - } - - insightObj.insert("memoryInspectionPaneSettings", memoryInspectionPaneSettingsObj); - return insightObj; -} diff --git a/src/SignalHandler/SignalHandler.cpp b/src/SignalHandler/SignalHandler.cpp index 43949496..0d593bf3 100644 --- a/src/SignalHandler/SignalHandler.cpp +++ b/src/SignalHandler/SignalHandler.cpp @@ -6,77 +6,78 @@ #include "src/Logger/Logger.hpp" #include "src/Exceptions/Exception.hpp" -using namespace Bloom; +namespace Bloom +{ + void SignalHandler::run() { + try { + this->startup(); + auto signalSet = this->getRegisteredSignalSet(); + int signalNumber = 0; -void SignalHandler::run() { - try { - this->startup(); - auto signalSet = this->getRegisteredSignalSet(); - int signalNumber = 0; - - Logger::debug("SignalHandler ready"); - while(Thread::getThreadState() == ThreadState::READY) { - if (sigwait(&signalSet, &signalNumber) == 0) { - Logger::debug("SIGNAL " + std::to_string(signalNumber) + " received"); - if (this->handlersMappedBySignalNum.contains(signalNumber)) { - // We have a registered handler for this signal. - this->handlersMappedBySignalNum.at(signalNumber)(); + Logger::debug("SignalHandler ready"); + while(Thread::getThreadState() == ThreadState::READY) { + if (sigwait(&signalSet, &signalNumber) == 0) { + Logger::debug("SIGNAL " + std::to_string(signalNumber) + " received"); + if (this->handlersMappedBySignalNum.contains(signalNumber)) { + // We have a registered handler for this signal. + this->handlersMappedBySignalNum.at(signalNumber)(); + } } } + + } catch (std::exception& exception) { + Logger::error("SignalHandler fatal error: " + std::string(exception.what())); } - } catch (std::exception& exception) { - Logger::error("SignalHandler fatal error: " + std::string(exception.what())); + Logger::info("Shutting down SignalHandler"); + Thread::setThreadState(ThreadState::STOPPED); } - Logger::info("Shutting down SignalHandler"); - Thread::setThreadState(ThreadState::STOPPED); -} + void SignalHandler::startup() { + this->setName("SH"); + Thread::setThreadState(ThreadState::STARTING); + Logger::debug("Starting SignalHandler"); + // Block all signal interrupts + auto signalSet = this->getRegisteredSignalSet(); + sigprocmask(SIG_SETMASK, &signalSet, NULL); -void SignalHandler::startup() { - this->setName("SH"); - Thread::setThreadState(ThreadState::STARTING); - Logger::debug("Starting SignalHandler"); - // Block all signal interrupts - auto signalSet = this->getRegisteredSignalSet(); - sigprocmask(SIG_SETMASK, &signalSet, NULL); + // Register handlers + this->handlersMappedBySignalNum.insert(std::pair( + SIGINT, + std::bind(&SignalHandler::triggerApplicationShutdown, this) + )); - // Register handlers - this->handlersMappedBySignalNum.insert(std::pair( - SIGINT, - std::bind(&SignalHandler::triggerApplicationShutdown, this) - )); + this->handlersMappedBySignalNum.insert(std::pair( + SIGTERM, + std::bind(&SignalHandler::triggerApplicationShutdown, this) + )); - this->handlersMappedBySignalNum.insert(std::pair( - SIGTERM, - std::bind(&SignalHandler::triggerApplicationShutdown, this) - )); - - // It's possible that the SignalHandler has been instructed to shutdown, before it could finish starting up. - if (this->getThreadState() != ThreadState::SHUTDOWN_INITIATED) { - Thread::setThreadState(ThreadState::READY); - } -} - -sigset_t SignalHandler::getRegisteredSignalSet() const { - sigset_t set = {}; - if (sigfillset(&set) == -1) { - throw Exceptions::Exception("sigfillset() failed - error number: " + std::to_string(errno)); + // It's possible that the SignalHandler has been instructed to shutdown, before it could finish starting up. + if (this->getThreadState() != ThreadState::SHUTDOWN_INITIATED) { + Thread::setThreadState(ThreadState::READY); + } } - return set; -} + sigset_t SignalHandler::getRegisteredSignalSet() const { + sigset_t set = {}; + if (sigfillset(&set) == -1) { + throw Exceptions::Exception("sigfillset() failed - error number: " + std::to_string(errno)); + } -void SignalHandler::triggerApplicationShutdown() { - Logger::warning("Shutdown signal received"); - this->shutdownSignalsReceived++; - - if (this->shutdownSignalsReceived > 1) { - // User has likely run out of patience - Logger::warning("Aborting immediately"); - exit(EXIT_FAILURE); + return set; } - Logger::info("Attempting clean shutdown"); - this->eventManager.triggerEvent(std::make_shared()); + void SignalHandler::triggerApplicationShutdown() { + Logger::warning("Shutdown signal received"); + this->shutdownSignalsReceived++; + + if (this->shutdownSignalsReceived > 1) { + // User has likely run out of patience + Logger::warning("Aborting immediately"); + exit(EXIT_FAILURE); + } + + Logger::info("Attempting clean shutdown"); + this->eventManager.triggerEvent(std::make_shared()); + } } diff --git a/src/TargetController/TargetController.cpp b/src/TargetController/TargetController.cpp index 07fa9448..e5cf529d 100644 --- a/src/TargetController/TargetController.cpp +++ b/src/TargetController/TargetController.cpp @@ -14,788 +14,791 @@ #include "src/Exceptions/TargetControllerStartupFailure.hpp" #include "src/Exceptions/InvalidConfig.hpp" -using namespace Bloom; -using namespace Bloom::Targets; -using namespace Bloom::Events; -using namespace Bloom::Exceptions; - -using Bloom::Targets::TargetRegisterDescriptor; -using Bloom::Targets::TargetRegisterDescriptors; - -void TargetController::run() { - try { - this->startup(); - - this->setThreadStateAndEmitEvent(ThreadState::READY); - Logger::debug("TargetController ready and waiting for events."); - - while (this->getThreadState() == ThreadState::READY) { - try { - if (this->state == TargetControllerState::ACTIVE) { - this->fireTargetEvents(); - } - - this->eventListener->waitAndDispatch(60); - - } catch (const DeviceFailure& exception) { - /* - * Upon a device failure, we assume Bloom has lost control of the debug tool. This could be the result - * of the user disconnecting the debug tool, or issuing a soft reset. The soft reset could have been - * issued via another application, without the user's knowledge. - * See https://github.com/navnavnav/Bloom/issues/3 for more on that. - * - * The TC will go into a suspended state and the DebugServer should terminate any active debug - * session. When the user attempts to start another debug session, we will try to re-connect to the - * debug tool. - */ - Logger::error("Device failure detected - " + exception.getMessage()); - Logger::error("Suspending TargetController"); - this->suspend(); - } - } - - } catch (const TargetControllerStartupFailure& exception) { - Logger::error("TargetController failed to startup. See below for errors:"); - Logger::error(exception.getMessage()); - - } catch (const Exception& exception) { - Logger::error("The TargetController encountered a fatal error. See below for errors:"); - Logger::error(exception.getMessage()); - - } catch (const std::exception& exception) { - Logger::error("The TargetController encountered a fatal error. See below for errors:"); - Logger::error(std::string(exception.what())); - } - - this->shutdown(); -} - -void TargetController::startup() { - this->setName("TC"); - Logger::info("Starting TargetController"); - this->setThreadState(ThreadState::STARTING); - this->blockAllSignalsOnCurrentThread(); - this->eventManager.registerListener(this->eventListener); - - // Install Bloom's udev rules if not already installed - TargetController::checkUdevRules(); - - // Register event handlers - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onStateReportRequest, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onShutdownTargetControllerEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onDebugSessionStartedEvent, this, std::placeholders::_1) - ); - - this->resume(); -} - -void TargetController::checkUdevRules() { - auto bloomRulesPath = std::string("/etc/udev/rules.d/99-bloom.rules"); - auto latestBloomRulesPath = Paths::resourcesDirPath() + "/UDevRules/99-bloom.rules"; - - if (!std::filesystem::exists(bloomRulesPath)) { - Logger::warning("Bloom udev rules missing - attempting installation"); - - // We can only install them if we're running as root - if (!Application::isRunningAsRoot()) { - Logger::error("Bloom udev rules missing - cannot install udev rules without root privileges.\n" - "Running Bloom once with root privileges will allow it to automatically install the udev rules. " - "Alternatively, instructions on manually installing the udev rules can be found " - "here: " + Paths::homeDomainName() + "/docs/getting-started\nBloom may fail to connect to some " - "debug tools until this is resolved."); - return; - } - - if (!std::filesystem::exists(latestBloomRulesPath)) { - // This shouldn't happen, but it can if someone has been messing with the installation files - Logger::error("Unable to install Bloom udev rules - \"" + latestBloomRulesPath + "\" does not exist."); - return; - } - - std::filesystem::copy(latestBloomRulesPath, bloomRulesPath); - Logger::warning("Bloom udev rules installed - a reconnect of the debug tool may be required " - "before the new udev rules come into effect."); - } -} - -void TargetController::shutdown() { - if (this->getThreadState() == ThreadState::STOPPED) { - return; - } - - try { - Logger::info("Shutting down TargetController"); - this->eventManager.deregisterListener(this->eventListener->getId()); - this->releaseHardware(); - - } catch (const std::exception& exception) { - this->target.reset(); - this->debugTool.reset(); - Logger::error("Failed to properly shutdown TargetController. Error: " + std::string(exception.what())); - } - - this->setThreadStateAndEmitEvent(ThreadState::STOPPED); -} - -void TargetController::suspend() { - if (this->getThreadState() != ThreadState::READY) { - return; - } - - Logger::debug("Suspending TargetController"); - - try { - this->releaseHardware(); - - } catch (const std::exception& exception) { - Logger::error("Failed to release connected debug tool and target resources. Error: " - + std::string(exception.what())); - } - - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - this->eventListener->deregisterCallbacksForEventType(); - - this->lastTargetState = TargetState::UNKNOWN; - this->cachedTargetDescriptor = std::nullopt; - this->registerDescriptorsByMemoryType.clear(); - this->registerAddressRangeByMemoryType.clear(); - - this->state = TargetControllerState::SUSPENDED; - this->eventManager.triggerEvent( - std::make_shared(this->state) - ); - - Logger::debug("TargetController suspended"); -} - -void TargetController::resume() { - this->acquireHardware(); - this->loadRegisterDescriptors(); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onDebugSessionFinishedEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onExtractTargetDescriptor, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onStopTargetExecutionEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onStepTargetExecutionEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onResumeTargetExecutionEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onReadRegistersEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onWriteRegistersEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onReadMemoryEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onWriteMemoryEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onSetBreakpointEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onRemoveBreakpointEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onSetProgramCounterEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onInsightStateChangedEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onRetrieveTargetPinStatesEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onSetPinStateEvent, this, std::placeholders::_1) - ); - - this->eventListener->registerCallbackForEventType( - std::bind(&TargetController::onRetrieveStackPointerEvent, this, std::placeholders::_1) - ); - - this->state = TargetControllerState::ACTIVE; - this->eventManager.triggerEvent( - std::make_shared(this->state) - ); - - if (this->target->getState() != TargetState::RUNNING) { - this->target->run(); - } -} - -void TargetController::acquireHardware() { - auto debugToolName = this->environmentConfig.debugToolConfig.name; - auto targetName = this->environmentConfig.targetConfig.name; - - auto supportedDebugTools = TargetController::getSupportedDebugTools(); - auto supportedTargets = TargetController::getSupportedTargets(); - - if (!supportedDebugTools.contains(debugToolName)) { - throw Exceptions::InvalidConfig( - "Debug tool name (\"" + debugToolName + "\") not recognised. Please check your configuration!" - ); - } - - if (!supportedTargets.contains(targetName)) { - throw Exceptions::InvalidConfig( - "Target name (\"" + targetName + "\") not recognised. Please check your configuration!" - ); - } - - // Initiate debug tool and target - this->debugTool = supportedDebugTools.at(debugToolName)(); - - Logger::info("Connecting to debug tool"); - this->debugTool->init(); - - Logger::info("Debug tool connected"); - Logger::info("Debug tool name: " + this->debugTool->getName()); - Logger::info("Debug tool serial: " + this->debugTool->getSerialNumber()); - - this->target = supportedTargets.at(targetName)(); - - if (!this->target->isDebugToolSupported(this->debugTool.get())) { - throw Exceptions::InvalidConfig( - "Debug tool (\"" + this->debugTool->getName() + "\") not supported " + - "by target (\"" + this->target->getName() + "\")." - ); - } - - this->target->setDebugTool(this->debugTool.get()); - this->target->preActivationConfigure(this->environmentConfig.targetConfig); - - Logger::info("Activating target"); - this->target->activate(); - Logger::info("Target activated"); - this->target->postActivationConfigure(); - - while (this->target->supportsPromotion()) { - auto promotedTarget = this->target->promote(); - - if (promotedTarget == nullptr - || std::type_index(typeid(*promotedTarget)) == std::type_index(typeid(*this->target)) - ) { - break; - } - - this->target = std::move(promotedTarget); - this->target->postPromotionConfigure(); - } - - Logger::info("Target ID: " + this->target->getHumanReadableId()); - Logger::info("Target name: " + this->target->getName()); -} - -void TargetController::releaseHardware() { - auto target = this->getTarget(); - auto debugTool = this->getDebugTool(); - - if (debugTool != nullptr && debugTool->isInitialised()) { - if (target != nullptr) { - /* - * We call deactivate() without checking if the target is activated. This will address any cases - * where a target is only partially activated (where the call to activate() failed). - */ - Logger::info("Deactivating target"); - target->deactivate(); - } - - Logger::info("Closing debug tool"); - debugTool->close(); - } - - this->debugTool.reset(); - this->target.reset(); -} - -void TargetController::loadRegisterDescriptors() { - auto& targetDescriptor = this->getTargetDescriptor(); - - for (const auto& [registerType, registerDescriptors] : targetDescriptor.registerDescriptorsByType) { - for (const auto& registerDescriptor : registerDescriptors) { - auto startAddress = registerDescriptor.startAddress.value_or(0); - auto endAddress = startAddress + (registerDescriptor.size - 1); - - if (!this->registerAddressRangeByMemoryType.contains(registerDescriptor.memoryType)) { - auto addressRange = TargetMemoryAddressRange(); - addressRange.startAddress = startAddress; - addressRange.endAddress = endAddress; - this->registerAddressRangeByMemoryType.insert( - std::pair(registerDescriptor.memoryType, addressRange) - ); - - } else { - auto& addressRange = this->registerAddressRangeByMemoryType.at(registerDescriptor.memoryType); - - if (startAddress < addressRange.startAddress) { - addressRange.startAddress = startAddress; - } - - if (endAddress > addressRange.endAddress) { - addressRange.endAddress = endAddress; +namespace Bloom +{ + using namespace Bloom::Targets; + using namespace Bloom::Events; + using namespace Bloom::Exceptions; + + void TargetController::run() { + try { + this->startup(); + + this->setThreadStateAndEmitEvent(ThreadState::READY); + Logger::debug("TargetController ready and waiting for events."); + + while (this->getThreadState() == ThreadState::READY) { + try { + if (this->state == TargetControllerState::ACTIVE) { + this->fireTargetEvents(); + } + + this->eventListener->waitAndDispatch(60); + + } catch (const DeviceFailure& exception) { + /* + * Upon a device failure, we assume Bloom has lost control of the debug tool. This could be the + * result of the user disconnecting the debug tool, or issuing a soft reset. The soft reset could + * have been issued via another application, without the user's knowledge. + * See https://github.com/navnavnav/Bloom/issues/3 for more on that. + * + * The TC will go into a suspended state and the DebugServer should terminate any active debug + * session. When the user attempts to start another debug session, we will try to re-connect to the + * debug tool. + */ + Logger::error("Device failure detected - " + exception.getMessage()); + Logger::error("Suspending TargetController"); + this->suspend(); } } - this->registerDescriptorsByMemoryType[registerDescriptor.memoryType].insert(registerDescriptor); - } - } -} + } catch (const TargetControllerStartupFailure& exception) { + Logger::error("TargetController failed to startup. See below for errors:"); + Logger::error(exception.getMessage()); -TargetRegisterDescriptors TargetController::getRegisterDescriptorsWithinAddressRange( - std::uint32_t startAddress, - std::uint32_t endAddress, - Targets::TargetMemoryType memoryType -) { - auto output = TargetRegisterDescriptors(); + } catch (const Exception& exception) { + Logger::error("The TargetController encountered a fatal error. See below for errors:"); + Logger::error(exception.getMessage()); - if (this->registerAddressRangeByMemoryType.contains(memoryType) - && this->registerDescriptorsByMemoryType.contains(memoryType) - ) { - auto& registersAddressRange = this->registerAddressRangeByMemoryType.at(memoryType); - - if ( - (startAddress <= registersAddressRange.startAddress && endAddress >= registersAddressRange.startAddress) - || (startAddress <= registersAddressRange.endAddress && endAddress >= registersAddressRange.startAddress) - ) { - auto& registerDescriptors = this->registerDescriptorsByMemoryType.at(memoryType); - - for (const auto& registerDescriptor : registerDescriptors) { - if (!registerDescriptor.startAddress.has_value() || registerDescriptor.size < 1) { - continue; - } - - auto registerStartAddress = registerDescriptor.startAddress.value(); - auto registerEndAddress = registerStartAddress + registerDescriptor.size; - - if ( - (startAddress <= registerStartAddress && endAddress >= registerStartAddress) - || (startAddress <= registerEndAddress && endAddress >= registerStartAddress) - ) { - output.insert(registerDescriptor); - } - } - } - } - - return output; -} - -void TargetController::fireTargetEvents() { - auto newTargetState = this->target->getState(); - - if (newTargetState != this->lastTargetState) { - this->lastTargetState = newTargetState; - - if (newTargetState == TargetState::STOPPED) { - Logger::debug("Target state changed - STOPPED"); - this->eventManager.triggerEvent(std::make_shared( - this->target->getProgramCounter(), - TargetBreakCause::UNKNOWN - )); + } catch (const std::exception& exception) { + Logger::error("The TargetController encountered a fatal error. See below for errors:"); + Logger::error(std::string(exception.what())); } - if (newTargetState == TargetState::RUNNING) { - Logger::debug("Target state changed - RUNNING"); - this->eventManager.triggerEvent(std::make_shared()); - } - } -} - -void TargetController::emitErrorEvent(int correlationId, const std::string& errorMessage) { - auto errorEvent = std::make_shared(); - errorEvent->correlationId = correlationId; - errorEvent->errorMessage = errorMessage; - this->eventManager.triggerEvent(errorEvent); -} - -Targets::TargetDescriptor& TargetController::getTargetDescriptor() { - if (!this->cachedTargetDescriptor.has_value()) { - this->cachedTargetDescriptor = this->target->getDescriptor(); + this->shutdown(); } - return this->cachedTargetDescriptor.value(); -} + void TargetController::startup() { + this->setName("TC"); + Logger::info("Starting TargetController"); + this->setThreadState(ThreadState::STARTING); + this->blockAllSignalsOnCurrentThread(); + this->eventManager.registerListener(this->eventListener); -void TargetController::onShutdownTargetControllerEvent(const Events::ShutdownTargetController&) { - this->shutdown(); -} + // Install Bloom's udev rules if not already installed + TargetController::checkUdevRules(); -void TargetController::onStateReportRequest(const Events::ReportTargetControllerState& event) { - auto stateEvent = std::make_shared(this->state); - stateEvent->correlationId = event.id; - this->eventManager.triggerEvent(stateEvent); -} + // Register event handlers + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onStateReportRequest, this, std::placeholders::_1) + ); -void TargetController::onExtractTargetDescriptor(const Events::ExtractTargetDescriptor& event) { - auto targetDescriptorExtracted = std::make_shared(); - targetDescriptorExtracted->targetDescriptor = this->getTargetDescriptor(); + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onShutdownTargetControllerEvent, this, std::placeholders::_1) + ); - targetDescriptorExtracted->correlationId = event.id; - this->eventManager.triggerEvent(targetDescriptorExtracted); -} - -void TargetController::onDebugSessionStartedEvent(const Events::DebugSessionStarted&) { - if (this->state == TargetControllerState::SUSPENDED) { - Logger::debug("Waking TargetController"); + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onDebugSessionStartedEvent, this, std::placeholders::_1) + ); this->resume(); - this->fireTargetEvents(); } - this->target->reset(); + void TargetController::checkUdevRules() { + auto bloomRulesPath = std::string("/etc/udev/rules.d/99-bloom.rules"); + auto latestBloomRulesPath = Paths::resourcesDirPath() + "/UDevRules/99-bloom.rules"; - if (this->target->getState() != TargetState::STOPPED) { - this->target->stop(); - } -} + if (!std::filesystem::exists(bloomRulesPath)) { + Logger::warning("Bloom udev rules missing - attempting installation"); -void TargetController::onDebugSessionFinishedEvent(const DebugSessionFinished&) { - if (this->target->getState() != TargetState::RUNNING) { - this->target->run(); - this->fireTargetEvents(); + // We can only install them if we're running as root + if (!Application::isRunningAsRoot()) { + Logger::error("Bloom udev rules missing - cannot install udev rules without root privileges.\n" + "Running Bloom once with root privileges will allow it to automatically install the udev rules. " + "Alternatively, instructions on manually installing the udev rules can be found " + "here: " + Paths::homeDomainName() + "/docs/getting-started\nBloom may fail to connect to some " + "debug tools until this is resolved."); + return; + } + + if (!std::filesystem::exists(latestBloomRulesPath)) { + // This shouldn't happen, but it can if someone has been messing with the installation files + Logger::error( + "Unable to install Bloom udev rules - \"" + latestBloomRulesPath + "\" does not exist." + ); + return; + } + + std::filesystem::copy(latestBloomRulesPath, bloomRulesPath); + Logger::warning("Bloom udev rules installed - a reconnect of the debug tool may be required " + "before the new udev rules come into effect."); + } } - if (this->environmentConfig.debugToolConfig.releasePostDebugSession) { - this->suspend(); - } -} - -void TargetController::onStopTargetExecutionEvent(const Events::StopTargetExecution& event) { - if (this->target->getState() != TargetState::STOPPED) { - this->target->stop(); - this->lastTargetState = TargetState::STOPPED; - } - - auto executionStoppedEvent = std::make_shared( - this->target->getProgramCounter(), - TargetBreakCause::UNKNOWN - ); - - executionStoppedEvent->correlationId = event.id; - this->eventManager.triggerEvent(executionStoppedEvent); -} - -void TargetController::onStepTargetExecutionEvent(const Events::StepTargetExecution& event) { - try { - if (this->target->getState() != TargetState::STOPPED) { - // We can't step the target if it's already running. - throw TargetOperationFailure("Target is already running"); + void TargetController::shutdown() { + if (this->getThreadState() == ThreadState::STOPPED) { + return; } - if (event.fromProgramCounter.has_value()) { - this->target->setProgramCounter(event.fromProgramCounter.value()); + try { + Logger::info("Shutting down TargetController"); + this->eventManager.deregisterListener(this->eventListener->getId()); + this->releaseHardware(); + + } catch (const std::exception& exception) { + this->target.reset(); + this->debugTool.reset(); + Logger::error( + "Failed to properly shutdown TargetController. Error: " + std::string(exception.what()) + ); } - this->target->step(); - this->lastTargetState = TargetState::RUNNING; - - auto executionResumedEvent = std::make_shared(); - executionResumedEvent->correlationId = event.id; - this->eventManager.triggerEvent(executionResumedEvent); - - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to step execution on target - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); + this->setThreadStateAndEmitEvent(ThreadState::STOPPED); } -} -void TargetController::onResumeTargetExecutionEvent(const Events::ResumeTargetExecution& event) { - try { + void TargetController::suspend() { + if (this->getThreadState() != ThreadState::READY) { + return; + } + + Logger::debug("Suspending TargetController"); + + try { + this->releaseHardware(); + + } catch (const std::exception& exception) { + Logger::error("Failed to release connected debug tool and target resources. Error: " + + std::string(exception.what())); + } + + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + this->eventListener->deregisterCallbacksForEventType(); + + this->lastTargetState = TargetState::UNKNOWN; + this->cachedTargetDescriptor = std::nullopt; + this->registerDescriptorsByMemoryType.clear(); + this->registerAddressRangeByMemoryType.clear(); + + this->state = TargetControllerState::SUSPENDED; + this->eventManager.triggerEvent( + std::make_shared(this->state) + ); + + Logger::debug("TargetController suspended"); + } + + void TargetController::resume() { + this->acquireHardware(); + this->loadRegisterDescriptors(); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onDebugSessionFinishedEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onExtractTargetDescriptor, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onStopTargetExecutionEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onStepTargetExecutionEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onResumeTargetExecutionEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onReadRegistersEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onWriteRegistersEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onReadMemoryEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onWriteMemoryEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onSetBreakpointEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onRemoveBreakpointEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onSetProgramCounterEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onInsightStateChangedEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onRetrieveTargetPinStatesEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onSetPinStateEvent, this, std::placeholders::_1) + ); + + this->eventListener->registerCallbackForEventType( + std::bind(&TargetController::onRetrieveStackPointerEvent, this, std::placeholders::_1) + ); + + this->state = TargetControllerState::ACTIVE; + this->eventManager.triggerEvent( + std::make_shared(this->state) + ); + if (this->target->getState() != TargetState::RUNNING) { + this->target->run(); + } + } + + void TargetController::acquireHardware() { + auto debugToolName = this->environmentConfig.debugToolConfig.name; + auto targetName = this->environmentConfig.targetConfig.name; + + auto supportedDebugTools = TargetController::getSupportedDebugTools(); + auto supportedTargets = TargetController::getSupportedTargets(); + + if (!supportedDebugTools.contains(debugToolName)) { + throw Exceptions::InvalidConfig( + "Debug tool name (\"" + debugToolName + "\") not recognised. Please check your configuration!" + ); + } + + if (!supportedTargets.contains(targetName)) { + throw Exceptions::InvalidConfig( + "Target name (\"" + targetName + "\") not recognised. Please check your configuration!" + ); + } + + // Initiate debug tool and target + this->debugTool = supportedDebugTools.at(debugToolName)(); + + Logger::info("Connecting to debug tool"); + this->debugTool->init(); + + Logger::info("Debug tool connected"); + Logger::info("Debug tool name: " + this->debugTool->getName()); + Logger::info("Debug tool serial: " + this->debugTool->getSerialNumber()); + + this->target = supportedTargets.at(targetName)(); + + if (!this->target->isDebugToolSupported(this->debugTool.get())) { + throw Exceptions::InvalidConfig( + "Debug tool (\"" + this->debugTool->getName() + "\") not supported " + + "by target (\"" + this->target->getName() + "\")." + ); + } + + this->target->setDebugTool(this->debugTool.get()); + this->target->preActivationConfigure(this->environmentConfig.targetConfig); + + Logger::info("Activating target"); + this->target->activate(); + Logger::info("Target activated"); + this->target->postActivationConfigure(); + + while (this->target->supportsPromotion()) { + auto promotedTarget = this->target->promote(); + + if (promotedTarget == nullptr + || std::type_index(typeid(*promotedTarget)) == std::type_index(typeid(*this->target)) + ) { + break; + } + + this->target = std::move(promotedTarget); + this->target->postPromotionConfigure(); + } + + Logger::info("Target ID: " + this->target->getHumanReadableId()); + Logger::info("Target name: " + this->target->getName()); + } + + void TargetController::releaseHardware() { + auto target = this->getTarget(); + auto debugTool = this->getDebugTool(); + + if (debugTool != nullptr && debugTool->isInitialised()) { + if (target != nullptr) { + /* + * We call deactivate() without checking if the target is activated. This will address any cases + * where a target is only partially activated (where the call to activate() failed). + */ + Logger::info("Deactivating target"); + target->deactivate(); + } + + Logger::info("Closing debug tool"); + debugTool->close(); + } + + this->debugTool.reset(); + this->target.reset(); + } + + void TargetController::loadRegisterDescriptors() { + auto& targetDescriptor = this->getTargetDescriptor(); + + for (const auto& [registerType, registerDescriptors] : targetDescriptor.registerDescriptorsByType) { + for (const auto& registerDescriptor : registerDescriptors) { + auto startAddress = registerDescriptor.startAddress.value_or(0); + auto endAddress = startAddress + (registerDescriptor.size - 1); + + if (!this->registerAddressRangeByMemoryType.contains(registerDescriptor.memoryType)) { + auto addressRange = TargetMemoryAddressRange(); + addressRange.startAddress = startAddress; + addressRange.endAddress = endAddress; + this->registerAddressRangeByMemoryType.insert( + std::pair(registerDescriptor.memoryType, addressRange) + ); + + } else { + auto& addressRange = this->registerAddressRangeByMemoryType.at(registerDescriptor.memoryType); + + if (startAddress < addressRange.startAddress) { + addressRange.startAddress = startAddress; + } + + if (endAddress > addressRange.endAddress) { + addressRange.endAddress = endAddress; + } + } + + this->registerDescriptorsByMemoryType[registerDescriptor.memoryType].insert(registerDescriptor); + } + } + } + + TargetRegisterDescriptors TargetController::getRegisterDescriptorsWithinAddressRange( + std::uint32_t startAddress, + std::uint32_t endAddress, + Targets::TargetMemoryType memoryType + ) { + auto output = TargetRegisterDescriptors(); + + if (this->registerAddressRangeByMemoryType.contains(memoryType) + && this->registerDescriptorsByMemoryType.contains(memoryType) + ) { + auto& registersAddressRange = this->registerAddressRangeByMemoryType.at(memoryType); + + if ( + (startAddress <= registersAddressRange.startAddress && endAddress >= registersAddressRange.startAddress) + || (startAddress <= registersAddressRange.endAddress && endAddress >= registersAddressRange.startAddress) + ) { + auto& registerDescriptors = this->registerDescriptorsByMemoryType.at(memoryType); + + for (const auto& registerDescriptor : registerDescriptors) { + if (!registerDescriptor.startAddress.has_value() || registerDescriptor.size < 1) { + continue; + } + + auto registerStartAddress = registerDescriptor.startAddress.value(); + auto registerEndAddress = registerStartAddress + registerDescriptor.size; + + if ( + (startAddress <= registerStartAddress && endAddress >= registerStartAddress) + || (startAddress <= registerEndAddress && endAddress >= registerStartAddress) + ) { + output.insert(registerDescriptor); + } + } + } + } + + return output; + } + + void TargetController::fireTargetEvents() { + auto newTargetState = this->target->getState(); + + if (newTargetState != this->lastTargetState) { + this->lastTargetState = newTargetState; + + if (newTargetState == TargetState::STOPPED) { + Logger::debug("Target state changed - STOPPED"); + this->eventManager.triggerEvent(std::make_shared( + this->target->getProgramCounter(), + TargetBreakCause::UNKNOWN + )); + } + + if (newTargetState == TargetState::RUNNING) { + Logger::debug("Target state changed - RUNNING"); + this->eventManager.triggerEvent(std::make_shared()); + } + } + } + + void TargetController::emitErrorEvent(int correlationId, const std::string& errorMessage) { + auto errorEvent = std::make_shared(); + errorEvent->correlationId = correlationId; + errorEvent->errorMessage = errorMessage; + this->eventManager.triggerEvent(errorEvent); + } + + Targets::TargetDescriptor& TargetController::getTargetDescriptor() { + if (!this->cachedTargetDescriptor.has_value()) { + this->cachedTargetDescriptor = this->target->getDescriptor(); + } + + return this->cachedTargetDescriptor.value(); + } + + void TargetController::onShutdownTargetControllerEvent(const Events::ShutdownTargetController&) { + this->shutdown(); + } + + void TargetController::onStateReportRequest(const Events::ReportTargetControllerState& event) { + auto stateEvent = std::make_shared(this->state); + stateEvent->correlationId = event.id; + this->eventManager.triggerEvent(stateEvent); + } + + void TargetController::onExtractTargetDescriptor(const Events::ExtractTargetDescriptor& event) { + auto targetDescriptorExtracted = std::make_shared(); + targetDescriptorExtracted->targetDescriptor = this->getTargetDescriptor(); + + targetDescriptorExtracted->correlationId = event.id; + this->eventManager.triggerEvent(targetDescriptorExtracted); + } + + void TargetController::onDebugSessionStartedEvent(const Events::DebugSessionStarted&) { + if (this->state == TargetControllerState::SUSPENDED) { + Logger::debug("Waking TargetController"); + + this->resume(); + this->fireTargetEvents(); + } + + this->target->reset(); + + if (this->target->getState() != TargetState::STOPPED) { + this->target->stop(); + } + } + + void TargetController::onDebugSessionFinishedEvent(const DebugSessionFinished&) { + if (this->target->getState() != TargetState::RUNNING) { + this->target->run(); + this->fireTargetEvents(); + } + + if (this->environmentConfig.debugToolConfig.releasePostDebugSession) { + this->suspend(); + } + } + + void TargetController::onStopTargetExecutionEvent(const Events::StopTargetExecution& event) { + if (this->target->getState() != TargetState::STOPPED) { + this->target->stop(); + this->lastTargetState = TargetState::STOPPED; + } + + auto executionStoppedEvent = std::make_shared( + this->target->getProgramCounter(), + TargetBreakCause::UNKNOWN + ); + + executionStoppedEvent->correlationId = event.id; + this->eventManager.triggerEvent(executionStoppedEvent); + } + + void TargetController::onStepTargetExecutionEvent(const Events::StepTargetExecution& event) { + try { + if (this->target->getState() != TargetState::STOPPED) { + // We can't step the target if it's already running. + throw TargetOperationFailure("Target is already running"); + } + if (event.fromProgramCounter.has_value()) { this->target->setProgramCounter(event.fromProgramCounter.value()); } - this->target->run(); + this->target->step(); this->lastTargetState = TargetState::RUNNING; + + auto executionResumedEvent = std::make_shared(); + executionResumedEvent->correlationId = event.id; + this->eventManager.triggerEvent(executionResumedEvent); + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to step execution on target - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); } - - auto executionResumedEvent = std::make_shared(); - executionResumedEvent->correlationId = event.id; - this->eventManager.triggerEvent(executionResumedEvent); - - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to resume execution on target - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); } -} -void TargetController::onReadRegistersEvent(const Events::RetrieveRegistersFromTarget& event) { - try { - auto registers = this->target->readRegisters(event.descriptors); - - if (registers.size() > 0) { - auto registersRetrievedEvent = std::make_shared(); - registersRetrievedEvent->correlationId = event.id; - registersRetrievedEvent->registers = registers; - this->eventManager.triggerEvent(registersRetrievedEvent); - } - - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to read registers from target - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); - } -} - -void TargetController::onWriteRegistersEvent(const Events::WriteRegistersToTarget& event) { - try { - this->target->writeRegisters(event.registers); - - auto registersWrittenEvent = std::make_shared(); - registersWrittenEvent->correlationId = event.id; - registersWrittenEvent->registers = event.registers; - - this->eventManager.triggerEvent(registersWrittenEvent); - - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to write registers to target - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); - } -} - -void TargetController::onReadMemoryEvent(const Events::RetrieveMemoryFromTarget& event) { - try { - auto memoryReadEvent = std::make_shared(); - memoryReadEvent->correlationId = event.id; - memoryReadEvent->data = this->target->readMemory( - event.memoryType, - event.startAddress, - event.bytes, - event.excludedAddressRanges - ); - - this->eventManager.triggerEvent(memoryReadEvent); - - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to read memory from target - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); - } -} - -void TargetController::onWriteMemoryEvent(const Events::WriteMemoryToTarget& event) { - try { - const auto& buffer = event.buffer; - const auto bufferSize = event.buffer.size(); - const auto bufferStartAddress = event.startAddress; - - this->target->writeMemory(event.memoryType, event.startAddress, event.buffer); - - auto memoryWrittenEvent = std::make_shared(); - memoryWrittenEvent->correlationId = event.id; - this->eventManager.triggerEvent(memoryWrittenEvent); - - if (this->eventManager.isEventTypeListenedFor(Events::RegistersWrittenToTarget::type) - && this->registerDescriptorsByMemoryType.contains(event.memoryType) - ) { - /* - * The memory type we just wrote to contains some number of registers - if we've written to any address - * that is known to store the value of a register, trigger a RegistersWrittenToTarget event - */ - const auto bufferEndAddress = static_cast(bufferStartAddress + (bufferSize - 1)); - auto registerDescriptors = this->getRegisterDescriptorsWithinAddressRange( - bufferStartAddress, - bufferEndAddress, - event.memoryType - ); - - if (!registerDescriptors.empty()) { - auto registersWrittenEvent = std::make_shared(); - registersWrittenEvent->correlationId = event.id; - - for (const auto& registerDescriptor : registerDescriptors) { - const auto registerSize = registerDescriptor.size; - const auto registerStartAddress = registerDescriptor.startAddress.value(); - const auto registerEndAddress = registerStartAddress + (registerSize - 1); - - if (registerStartAddress < bufferStartAddress || registerEndAddress > bufferEndAddress) { - continue; - } - - const auto bufferBeginIt = buffer.begin() + (registerStartAddress - bufferStartAddress); - registersWrittenEvent->registers.emplace_back(TargetRegister( - registerDescriptor, - TargetMemoryBuffer(bufferBeginIt, bufferBeginIt + registerSize) - )); + void TargetController::onResumeTargetExecutionEvent(const Events::ResumeTargetExecution& event) { + try { + if (this->target->getState() != TargetState::RUNNING) { + if (event.fromProgramCounter.has_value()) { + this->target->setProgramCounter(event.fromProgramCounter.value()); } - this->eventManager.triggerEvent(registersWrittenEvent); + this->target->run(); + this->lastTargetState = TargetState::RUNNING; } + + auto executionResumedEvent = std::make_shared(); + executionResumedEvent->correlationId = event.id; + this->eventManager.triggerEvent(executionResumedEvent); + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to resume execution on target - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); } - - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to write memory to target - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); } -} -void TargetController::onSetBreakpointEvent(const Events::SetBreakpointOnTarget& event) { - try { - this->target->setBreakpoint(event.breakpoint.address); - auto breakpointSetEvent = std::make_shared(); - breakpointSetEvent->correlationId = event.id; + void TargetController::onReadRegistersEvent(const Events::RetrieveRegistersFromTarget& event) { + try { + auto registers = this->target->readRegisters(event.descriptors); - this->eventManager.triggerEvent(breakpointSetEvent); + if (registers.size() > 0) { + auto registersRetrievedEvent = std::make_shared(); + registersRetrievedEvent->correlationId = event.id; + registersRetrievedEvent->registers = registers; + this->eventManager.triggerEvent(registersRetrievedEvent); + } - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to set breakpoint on target - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to read registers from target - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); + } } -} -void TargetController::onRemoveBreakpointEvent(const Events::RemoveBreakpointOnTarget& event) { - try { - this->target->removeBreakpoint(event.breakpoint.address); - auto breakpointRemovedEvent = std::make_shared(); - breakpointRemovedEvent->correlationId = event.id; + void TargetController::onWriteRegistersEvent(const Events::WriteRegistersToTarget& event) { + try { + this->target->writeRegisters(event.registers); - this->eventManager.triggerEvent(breakpointRemovedEvent); + auto registersWrittenEvent = std::make_shared(); + registersWrittenEvent->correlationId = event.id; + registersWrittenEvent->registers = event.registers; - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to remove breakpoint on target - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); + this->eventManager.triggerEvent(registersWrittenEvent); + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to write registers to target - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); + } } -} -void TargetController::onSetProgramCounterEvent(const Events::SetProgramCounterOnTarget& event) { - try { - if (this->target->getState() != TargetState::STOPPED) { - throw TargetOperationFailure( - "Invalid target state - target must be stopped before the program counter can be updated" + void TargetController::onReadMemoryEvent(const Events::RetrieveMemoryFromTarget& event) { + try { + auto memoryReadEvent = std::make_shared(); + memoryReadEvent->correlationId = event.id; + memoryReadEvent->data = this->target->readMemory( + event.memoryType, + event.startAddress, + event.bytes, + event.excludedAddressRanges ); + + this->eventManager.triggerEvent(memoryReadEvent); + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to read memory from target - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); } - - this->target->setProgramCounter(event.address); - auto programCounterSetEvent = std::make_shared(); - programCounterSetEvent->correlationId = event.id; - - this->eventManager.triggerEvent(programCounterSetEvent); - - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to set program counter on target - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); } -} -// TODO: remove this -void TargetController::onInsightStateChangedEvent(const Events::InsightThreadStateChanged& event) { - if (event.getState() == ThreadState::READY) { - /* - * Insight has just started up. - * - * Refresh the target state and kick off a target stop/resume execution event. Setting the lastTargetState - * to UNKNOWN will be enough to do this. See TargetController::fireTargetEvents(). - */ - this->lastTargetState = TargetState::UNKNOWN; - } -} + void TargetController::onWriteMemoryEvent(const Events::WriteMemoryToTarget& event) { + try { + const auto& buffer = event.buffer; + const auto bufferSize = event.buffer.size(); + const auto bufferStartAddress = event.startAddress; -void TargetController::onRetrieveTargetPinStatesEvent(const Events::RetrieveTargetPinStates& event) { - try { - if (this->target->getState() != TargetState::STOPPED) { - throw TargetOperationFailure( - "Invalid target state - target must be stopped before pin states can be retrieved" - ); + this->target->writeMemory(event.memoryType, event.startAddress, event.buffer); + + auto memoryWrittenEvent = std::make_shared(); + memoryWrittenEvent->correlationId = event.id; + this->eventManager.triggerEvent(memoryWrittenEvent); + + if (this->eventManager.isEventTypeListenedFor(Events::RegistersWrittenToTarget::type) + && this->registerDescriptorsByMemoryType.contains(event.memoryType) + ) { + /* + * The memory type we just wrote to contains some number of registers - if we've written to any address + * that is known to store the value of a register, trigger a RegistersWrittenToTarget event + */ + const auto bufferEndAddress = static_cast(bufferStartAddress + (bufferSize - 1)); + auto registerDescriptors = this->getRegisterDescriptorsWithinAddressRange( + bufferStartAddress, + bufferEndAddress, + event.memoryType + ); + + if (!registerDescriptors.empty()) { + auto registersWrittenEvent = std::make_shared(); + registersWrittenEvent->correlationId = event.id; + + for (const auto& registerDescriptor : registerDescriptors) { + const auto registerSize = registerDescriptor.size; + const auto registerStartAddress = registerDescriptor.startAddress.value(); + const auto registerEndAddress = registerStartAddress + (registerSize - 1); + + if (registerStartAddress < bufferStartAddress || registerEndAddress > bufferEndAddress) { + continue; + } + + const auto bufferBeginIt = buffer.begin() + (registerStartAddress - bufferStartAddress); + registersWrittenEvent->registers.emplace_back(TargetRegister( + registerDescriptor, + TargetMemoryBuffer(bufferBeginIt, bufferBeginIt + registerSize) + )); + } + + this->eventManager.triggerEvent(registersWrittenEvent); + } + } + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to write memory to target - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); } - - auto pinStatesRetrieved = std::make_shared(); - pinStatesRetrieved->correlationId = event.id; - pinStatesRetrieved->variantId = event.variantId; - pinStatesRetrieved->pinSatesByNumber = this->target->getPinStates(event.variantId); - - this->eventManager.triggerEvent(pinStatesRetrieved); - - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to retrieve target pin states - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); } -} -void TargetController::onSetPinStateEvent(const Events::SetTargetPinState& event) { - try { - if (this->target->getState() != TargetState::STOPPED) { - throw TargetOperationFailure( - "Invalid target state - target must be stopped before pin state can be set" - ); + void TargetController::onSetBreakpointEvent(const Events::SetBreakpointOnTarget& event) { + try { + this->target->setBreakpoint(event.breakpoint.address); + auto breakpointSetEvent = std::make_shared(); + breakpointSetEvent->correlationId = event.id; + + this->eventManager.triggerEvent(breakpointSetEvent); + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to set breakpoint on target - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); } - - this->target->setPinState(event.pinDescriptor, event.pinState); - - auto pinStatesUpdateEvent = std::make_shared(); - pinStatesUpdateEvent->correlationId = event.id; - pinStatesUpdateEvent->variantId = event.pinDescriptor.variantId; - pinStatesUpdateEvent->pinSatesByNumber = { - {event.pinDescriptor.number, event.pinState} - }; - - this->eventManager.triggerEvent(pinStatesUpdateEvent); - - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to set target pin state for pin " + event.pinDescriptor.name + " - " - + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); } -} -void TargetController::onRetrieveStackPointerEvent(const Events::RetrieveStackPointerFromTarget& event) { - try { - if (this->target->getState() != TargetState::STOPPED) { - throw TargetOperationFailure( - "Invalid target state - target must be stopped before stack pointer can be retrieved" - ); + void TargetController::onRemoveBreakpointEvent(const Events::RemoveBreakpointOnTarget& event) { + try { + this->target->removeBreakpoint(event.breakpoint.address); + auto breakpointRemovedEvent = std::make_shared(); + breakpointRemovedEvent->correlationId = event.id; + + this->eventManager.triggerEvent(breakpointRemovedEvent); + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to remove breakpoint on target - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); } + } - auto stackPointerRetrieved = std::make_shared(); - stackPointerRetrieved->correlationId = event.id; - stackPointerRetrieved->stackPointer = this->target->getStackPointer(); + void TargetController::onSetProgramCounterEvent(const Events::SetProgramCounterOnTarget& event) { + try { + if (this->target->getState() != TargetState::STOPPED) { + throw TargetOperationFailure( + "Invalid target state - target must be stopped before the program counter can be updated" + ); + } - this->eventManager.triggerEvent(stackPointerRetrieved); + this->target->setProgramCounter(event.address); + auto programCounterSetEvent = std::make_shared(); + programCounterSetEvent->correlationId = event.id; - } catch (const TargetOperationFailure& exception) { - Logger::error("Failed to retrieve stack pointer value from target - " + exception.getMessage()); - this->emitErrorEvent(event.id, exception.getMessage()); + this->eventManager.triggerEvent(programCounterSetEvent); + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to set program counter on target - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); + } + } + + // TODO: remove this + void TargetController::onInsightStateChangedEvent(const Events::InsightThreadStateChanged& event) { + if (event.getState() == ThreadState::READY) { + /* + * Insight has just started up. + * + * Refresh the target state and kick off a target stop/resume execution event. Setting the lastTargetState + * to UNKNOWN will be enough to do this. See TargetController::fireTargetEvents(). + */ + this->lastTargetState = TargetState::UNKNOWN; + } + } + + void TargetController::onRetrieveTargetPinStatesEvent(const Events::RetrieveTargetPinStates& event) { + try { + if (this->target->getState() != TargetState::STOPPED) { + throw TargetOperationFailure( + "Invalid target state - target must be stopped before pin states can be retrieved" + ); + } + + auto pinStatesRetrieved = std::make_shared(); + pinStatesRetrieved->correlationId = event.id; + pinStatesRetrieved->variantId = event.variantId; + pinStatesRetrieved->pinSatesByNumber = this->target->getPinStates(event.variantId); + + this->eventManager.triggerEvent(pinStatesRetrieved); + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to retrieve target pin states - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); + } + } + + void TargetController::onSetPinStateEvent(const Events::SetTargetPinState& event) { + try { + if (this->target->getState() != TargetState::STOPPED) { + throw TargetOperationFailure( + "Invalid target state - target must be stopped before pin state can be set" + ); + } + + this->target->setPinState(event.pinDescriptor, event.pinState); + + auto pinStatesUpdateEvent = std::make_shared(); + pinStatesUpdateEvent->correlationId = event.id; + pinStatesUpdateEvent->variantId = event.pinDescriptor.variantId; + pinStatesUpdateEvent->pinSatesByNumber = { + {event.pinDescriptor.number, event.pinState} + }; + + this->eventManager.triggerEvent(pinStatesUpdateEvent); + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to set target pin state for pin " + event.pinDescriptor.name + " - " + + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); + } + } + + void TargetController::onRetrieveStackPointerEvent(const Events::RetrieveStackPointerFromTarget& event) { + try { + if (this->target->getState() != TargetState::STOPPED) { + throw TargetOperationFailure( + "Invalid target state - target must be stopped before stack pointer can be retrieved" + ); + } + + auto stackPointerRetrieved = std::make_shared(); + stackPointerRetrieved->correlationId = event.id; + stackPointerRetrieved->stackPointer = this->target->getStackPointer(); + + this->eventManager.triggerEvent(stackPointerRetrieved); + + } catch (const TargetOperationFailure& exception) { + Logger::error("Failed to retrieve stack pointer value from target - " + exception.getMessage()); + this->emitErrorEvent(event.id, exception.getMessage()); + } } } diff --git a/src/TargetController/TargetControllerConsole.cpp b/src/TargetController/TargetControllerConsole.cpp index b2cb1e95..b6d4b407 100644 --- a/src/TargetController/TargetControllerConsole.cpp +++ b/src/TargetController/TargetControllerConsole.cpp @@ -3,139 +3,141 @@ #include "src/EventManager/Events/Events.hpp" #include "src/Logger/Logger.hpp" -using namespace Bloom; -using namespace Bloom::Targets; -using namespace Bloom::Events; -using namespace Bloom::Exceptions; +namespace Bloom +{ + using namespace Bloom::Targets; + using namespace Bloom::Events; + using namespace Bloom::Exceptions; -TargetControllerConsole::TargetControllerConsole(EventManager& eventManager, EventListener& eventListener) - :eventManager(eventManager), eventListener(eventListener) {} + TargetControllerConsole::TargetControllerConsole(EventManager& eventManager, EventListener& eventListener) + :eventManager(eventManager), eventListener(eventListener) {} -TargetControllerState TargetControllerConsole::getTargetControllerState() { - return this->triggerTargetControllerEventAndWaitForResponse( - std::make_shared() - )->state; -} - -bool TargetControllerConsole::isTargetControllerInService() noexcept { - try { - return this->getTargetControllerState() == TargetControllerState::ACTIVE; - - } catch (...) { - return false; - } -} - -Targets::TargetDescriptor TargetControllerConsole::getTargetDescriptor() { - return this->triggerTargetControllerEventAndWaitForResponse( - std::make_shared() - )->targetDescriptor; -} - -void TargetControllerConsole::stopTargetExecution() { - this->triggerTargetControllerEventAndWaitForResponse(std::make_shared()); -} - -void TargetControllerConsole::continueTargetExecution(std::optional fromAddress) { - auto resumeExecutionEvent = std::make_shared(); - - if (fromAddress.has_value()) { - resumeExecutionEvent->fromProgramCounter = fromAddress.value(); + TargetControllerState TargetControllerConsole::getTargetControllerState() { + return this->triggerTargetControllerEventAndWaitForResponse( + std::make_shared() + )->state; } - this->triggerTargetControllerEventAndWaitForResponse(resumeExecutionEvent); -} + bool TargetControllerConsole::isTargetControllerInService() noexcept { + try { + return this->getTargetControllerState() == TargetControllerState::ACTIVE; -void TargetControllerConsole::stepTargetExecution(std::optional fromAddress) { - auto stepExecutionEvent = std::make_shared(); - - if (fromAddress.has_value()) { - stepExecutionEvent->fromProgramCounter = fromAddress.value(); + } catch (...) { + return false; + } } - this->triggerTargetControllerEventAndWaitForResponse(stepExecutionEvent); -} - -TargetRegisters TargetControllerConsole::readRegisters(const TargetRegisterDescriptors& descriptors) { - auto readRegistersEvent = std::make_shared(); - readRegistersEvent->descriptors = descriptors; - - return this->triggerTargetControllerEventAndWaitForResponse(readRegistersEvent)->registers; -} - -void TargetControllerConsole::writeRegisters(const TargetRegisters& registers) { - auto event = std::make_shared(); - event->registers = std::move(registers); - - this->triggerTargetControllerEventAndWaitForResponse(event); -} - -TargetMemoryBuffer TargetControllerConsole::readMemory( - TargetMemoryType memoryType, - std::uint32_t startAddress, - std::uint32_t bytes, - const std::set& excludedAddressRanges -) { - auto readMemoryEvent = std::make_shared(); - readMemoryEvent->memoryType = memoryType; - readMemoryEvent->startAddress = startAddress; - readMemoryEvent->bytes = bytes; - readMemoryEvent->excludedAddressRanges = excludedAddressRanges; - - return this->triggerTargetControllerEventAndWaitForResponse(readMemoryEvent)->data; -} - -void TargetControllerConsole::writeMemory( - TargetMemoryType memoryType, - std::uint32_t startAddress, - const TargetMemoryBuffer& buffer -) { - auto writeMemoryEvent = std::make_shared(); - writeMemoryEvent->memoryType = memoryType; - writeMemoryEvent->startAddress = startAddress; - writeMemoryEvent->buffer = buffer; - - this->triggerTargetControllerEventAndWaitForResponse(writeMemoryEvent); -} - -void TargetControllerConsole::setBreakpoint(TargetBreakpoint breakpoint) { - auto event = std::make_shared(); - event->breakpoint = breakpoint; - - this->triggerTargetControllerEventAndWaitForResponse(event); -} - -void TargetControllerConsole::removeBreakpoint(TargetBreakpoint breakpoint) { - auto event = std::make_shared(); - event->breakpoint = breakpoint; - - this->triggerTargetControllerEventAndWaitForResponse(event); -} - -void TargetControllerConsole::requestPinStates(int variantId) { - auto requestEvent = std::make_shared(); - requestEvent->variantId = variantId; - - this->eventManager.triggerEvent(requestEvent); -} - -Targets::TargetPinStateMappingType TargetControllerConsole::getPinStates(int variantId) { - auto requestEvent = std::make_shared(); - requestEvent->variantId = variantId; - - return this->triggerTargetControllerEventAndWaitForResponse(requestEvent)->pinSatesByNumber; -} - -void TargetControllerConsole::setPinState(TargetPinDescriptor pinDescriptor, TargetPinState pinState) { - auto updateEvent = std::make_shared(); - updateEvent->pinDescriptor = std::move(pinDescriptor); - updateEvent->pinState = pinState; - - this->triggerTargetControllerEventAndWaitForResponse(updateEvent); -} - -std::uint32_t TargetControllerConsole::getStackPointer() { - return this->triggerTargetControllerEventAndWaitForResponse( - std::make_shared() - )->stackPointer; + Targets::TargetDescriptor TargetControllerConsole::getTargetDescriptor() { + return this->triggerTargetControllerEventAndWaitForResponse( + std::make_shared() + )->targetDescriptor; + } + + void TargetControllerConsole::stopTargetExecution() { + this->triggerTargetControllerEventAndWaitForResponse(std::make_shared()); + } + + void TargetControllerConsole::continueTargetExecution(std::optional fromAddress) { + auto resumeExecutionEvent = std::make_shared(); + + if (fromAddress.has_value()) { + resumeExecutionEvent->fromProgramCounter = fromAddress.value(); + } + + this->triggerTargetControllerEventAndWaitForResponse(resumeExecutionEvent); + } + + void TargetControllerConsole::stepTargetExecution(std::optional fromAddress) { + auto stepExecutionEvent = std::make_shared(); + + if (fromAddress.has_value()) { + stepExecutionEvent->fromProgramCounter = fromAddress.value(); + } + + this->triggerTargetControllerEventAndWaitForResponse(stepExecutionEvent); + } + + TargetRegisters TargetControllerConsole::readRegisters(const TargetRegisterDescriptors& descriptors) { + auto readRegistersEvent = std::make_shared(); + readRegistersEvent->descriptors = descriptors; + + return this->triggerTargetControllerEventAndWaitForResponse(readRegistersEvent)->registers; + } + + void TargetControllerConsole::writeRegisters(const TargetRegisters& registers) { + auto event = std::make_shared(); + event->registers = std::move(registers); + + this->triggerTargetControllerEventAndWaitForResponse(event); + } + + TargetMemoryBuffer TargetControllerConsole::readMemory( + TargetMemoryType memoryType, + std::uint32_t startAddress, + std::uint32_t bytes, + const std::set& excludedAddressRanges + ) { + auto readMemoryEvent = std::make_shared(); + readMemoryEvent->memoryType = memoryType; + readMemoryEvent->startAddress = startAddress; + readMemoryEvent->bytes = bytes; + readMemoryEvent->excludedAddressRanges = excludedAddressRanges; + + return this->triggerTargetControllerEventAndWaitForResponse(readMemoryEvent)->data; + } + + void TargetControllerConsole::writeMemory( + TargetMemoryType memoryType, + std::uint32_t startAddress, + const TargetMemoryBuffer& buffer + ) { + auto writeMemoryEvent = std::make_shared(); + writeMemoryEvent->memoryType = memoryType; + writeMemoryEvent->startAddress = startAddress; + writeMemoryEvent->buffer = buffer; + + this->triggerTargetControllerEventAndWaitForResponse(writeMemoryEvent); + } + + void TargetControllerConsole::setBreakpoint(TargetBreakpoint breakpoint) { + auto event = std::make_shared(); + event->breakpoint = breakpoint; + + this->triggerTargetControllerEventAndWaitForResponse(event); + } + + void TargetControllerConsole::removeBreakpoint(TargetBreakpoint breakpoint) { + auto event = std::make_shared(); + event->breakpoint = breakpoint; + + this->triggerTargetControllerEventAndWaitForResponse(event); + } + + void TargetControllerConsole::requestPinStates(int variantId) { + auto requestEvent = std::make_shared(); + requestEvent->variantId = variantId; + + this->eventManager.triggerEvent(requestEvent); + } + + Targets::TargetPinStateMappingType TargetControllerConsole::getPinStates(int variantId) { + auto requestEvent = std::make_shared(); + requestEvent->variantId = variantId; + + return this->triggerTargetControllerEventAndWaitForResponse(requestEvent)->pinSatesByNumber; + } + + void TargetControllerConsole::setPinState(TargetPinDescriptor pinDescriptor, TargetPinState pinState) { + auto updateEvent = std::make_shared(); + updateEvent->pinDescriptor = std::move(pinDescriptor); + updateEvent->pinState = pinState; + + this->triggerTargetControllerEventAndWaitForResponse(updateEvent); + } + + std::uint32_t TargetControllerConsole::getStackPointer() { + return this->triggerTargetControllerEventAndWaitForResponse( + std::make_shared() + )->stackPointer; + } } diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp index bfbc824d..e4cb8de6 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp +++ b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp @@ -18,593 +18,605 @@ #include "Mega/Mega.hpp" #include "Tiny/Tiny.hpp" -using namespace Bloom; -using namespace Bloom::Targets; -using namespace Bloom::Targets::Microchip::Avr; -using namespace Bloom::Targets::Microchip::Avr::Avr8Bit; -using namespace Exceptions; +namespace Bloom::Targets::Microchip::Avr::Avr8Bit +{ + using namespace Exceptions; -void Avr8::preActivationConfigure(const TargetConfig& targetConfig) { - Target::preActivationConfigure(targetConfig); + void Avr8::preActivationConfigure(const TargetConfig& targetConfig) { + Target::preActivationConfigure(targetConfig); - if (this->family.has_value()) { - this->avr8Interface->setFamily(this->family.value()); - } - - this->avr8Interface->configure(targetConfig); -} - -void Avr8::postActivationConfigure() { - if (!this->targetDescriptionFile.has_value()) { - this->loadTargetDescriptionFile(); - this->initFromTargetDescriptionFile(); - } - - /* - * The signature obtained from the device should match what is in the target description file - * - * We don't use this->getId() here as that could return the ID that was extracted from the target description file - * (which it would, if the user specified the exact target name in their project config - see Avr8::getId() and - * TargetController::getSupportedTargets() for more). - */ - auto targetSignature = this->avr8Interface->getDeviceId(); - auto tdSignature = this->targetDescriptionFile->getTargetSignature(); - - if (targetSignature != tdSignature) { - throw Exception("Failed to validate connected target - target signature mismatch.\nThe target signature" - " (\"" + targetSignature.toHex() + "\") does not match the AVR8 target description signature (\"" - + tdSignature.toHex() + "\"). This will likely be due to an incorrect target name in the configuration file" - + " (bloom.json)." - ); - } -} - -void Avr8::postPromotionConfigure() { - if (!this->family.has_value()) { - throw Exception("Failed to resolve AVR8 family"); - } - - this->avr8Interface->setFamily(this->family.value()); - this->avr8Interface->setTargetParameters(this->targetParameters.value()); -} - -void Avr8::activate() { - if (this->isActivated()) { - return; - } - - this->avr8Interface->init(); - - if (this->targetDescriptionFile.has_value()) { - this->avr8Interface->setTargetParameters(this->targetParameters.value()); - } - - this->avr8Interface->activate(); - this->activated = true; -} - -void Avr8::deactivate() { - try { - this->avr8Interface->deactivate(); - this->activated = false; - - } catch (const Exception& exception) { - Logger::error("Failed to deactivate AVR8 target - " + exception.getMessage()); - } -} - -std::unique_ptr Avr8::promote() { - std::unique_ptr promoted = nullptr; - - if (this->family.has_value()) { - // Promote generic AVR8 target to correct family. - switch (this->family.value()) { - case Family::XMEGA: { - Logger::info("AVR8 target promoted to XMega target"); - promoted = std::make_unique(*this); - break; - } - case Family::MEGA: { - Logger::info("AVR8 target promoted to megaAVR target"); - promoted = std::make_unique(*this); - break; - } - case Family::TINY: { - Logger::info("AVR8 target promoted to tinyAVR target"); - promoted = std::make_unique(*this); - break; - } - default: { - break; - } - } - } - - return promoted; -} - -TargetDescriptor Avr8Bit::Avr8::getDescriptor() { - auto descriptor = TargetDescriptor(); - descriptor.id = this->getHumanReadableId(); - descriptor.name = this->getName(); - descriptor.registerDescriptorsByType = this->targetRegisterDescriptorsByType; - descriptor.memoryDescriptorsByType = this->targetMemoryDescriptorsByType; - - std::transform( - this->targetVariantsById.begin(), - this->targetVariantsById.end(), - std::back_inserter(descriptor.variants), - [] (auto& variantToIdPair) { - return variantToIdPair.second; - } - ); - - return descriptor; -} - -void Avr8::run() { - this->avr8Interface->run(); -} - -void Avr8::stop() { - this->avr8Interface->stop(); -} - -void Avr8::step() { - this->avr8Interface->step(); -} - -void Avr8::reset() { - this->avr8Interface->reset(); -} - -void Avr8::setBreakpoint(std::uint32_t address) { - this->avr8Interface->setBreakpoint(address); -} - -void Avr8::removeBreakpoint(std::uint32_t address) { - this->avr8Interface->clearBreakpoint(address); -} - -void Avr8::clearAllBreakpoints() { - this->avr8Interface->clearAllBreakpoints(); -} - -void Avr8::writeRegisters(TargetRegisters registers) { - for (auto registerIt = registers.begin(); registerIt != registers.end();) { - if (registerIt->descriptor.type == TargetRegisterType::PROGRAM_COUNTER) { - auto programCounterBytes = registerIt->value; - - if (programCounterBytes.size() < 4) { - // All PC register values should be at least 4 bytes in size - programCounterBytes.insert(programCounterBytes.begin(), 4 - programCounterBytes.size(), 0x00); - } - - this->setProgramCounter(static_cast( - programCounterBytes[0] << 24 - | programCounterBytes[1] << 16 - | programCounterBytes[2] << 8 - | programCounterBytes[3] - )); - - registerIt = registers.erase(registerIt); - - } else { - registerIt++; - } - } - - if (!registers.empty()) { - this->avr8Interface->writeRegisters(registers); - } -} - -TargetRegisters Avr8::readRegisters(TargetRegisterDescriptors descriptors) { - TargetRegisters registers; - - for (auto registerDescriptorIt = descriptors.begin(); registerDescriptorIt != descriptors.end();) { - const auto& descriptor = *registerDescriptorIt; - - if (descriptor.type == TargetRegisterType::PROGRAM_COUNTER) { - registers.push_back(this->getProgramCounterRegister()); - - registerDescriptorIt = descriptors.erase(registerDescriptorIt); - - } else { - registerDescriptorIt++; - } - } - - if (!descriptors.empty()) { - auto otherRegisters = this->avr8Interface->readRegisters(descriptors); - registers.insert(registers.end(), otherRegisters.begin(), otherRegisters.end()); - } - - return registers; -} - -TargetMemoryBuffer Avr8::readMemory( - TargetMemoryType memoryType, - std::uint32_t startAddress, - std::uint32_t bytes, - const std::set& excludedAddressRanges -) { - return this->avr8Interface->readMemory(memoryType, startAddress, bytes, excludedAddressRanges); -} - -void Avr8::writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) { - this->avr8Interface->writeMemory(memoryType, startAddress, buffer); -} - -TargetState Avr8::getState() { - return this->avr8Interface->getTargetState(); -} - -std::uint32_t Avr8::getProgramCounter() { - return this->avr8Interface->getProgramCounter(); -} - -TargetRegister Avr8::getProgramCounterRegister() { - auto programCounter = this->getProgramCounter(); - - return TargetRegister(TargetRegisterDescriptor(TargetRegisterType::PROGRAM_COUNTER), { - static_cast(programCounter >> 24), - static_cast(programCounter >> 16), - static_cast(programCounter >> 8), - static_cast(programCounter), - }); -} - -void Avr8::setProgramCounter(std::uint32_t programCounter) { - this->avr8Interface->setProgramCounter(programCounter); -} - -std::uint32_t Avr8::getStackPointer() { - const auto stackPointerRegister = this->readRegisters( - {this->targetRegisterDescriptorsByType.at(TargetRegisterType::STACK_POINTER)} - ).front(); - - std::uint32_t stackPointer = 0; - for (std::size_t i = 0; i < stackPointerRegister.size() && i < 4; i++) { - stackPointer = (stackPointer << (8 * i)) | stackPointerRegister.value[i]; - } - - return stackPointer; -} - -std::map Avr8::getPinStates(int variantId) { - if (!this->targetVariantsById.contains(variantId)) { - throw Exception("Invalid target variant ID"); - } - - std::map output; - auto& variant = this->targetVariantsById.at(variantId); - - /* - * To prevent the number of memory reads we perform here, we cache the data and map it by start address. - * - * This way, we only perform 3 memory reads for a target variant with 3 ports - one per port (instead of one - * per pin). - * - * We may be able to make this more efficient by combining reads for ports with aligned memory addresses. This will - * be considered when the need for it becomes apparent. - */ - std::map cachedMemoryByStartAddress; - auto readMemoryBitset = [this, &cachedMemoryByStartAddress] (std::uint16_t startAddress) { - if (!cachedMemoryByStartAddress.contains(startAddress)) { - cachedMemoryByStartAddress.insert( - std::pair( - startAddress, - this->readMemory(TargetMemoryType::RAM, startAddress, 1) - ) - ); + if (this->family.has_value()) { + this->avr8Interface->setFamily(this->family.value()); } - return std::bitset::digits>( - cachedMemoryByStartAddress.at(startAddress).at(0) - ); - }; - - for (const auto& [pinNumber, pinDescriptor] : variant.pinDescriptorsByNumber) { - if (this->padDescriptorsByName.contains(pinDescriptor.padName)) { - auto& pad = this->padDescriptorsByName.at(pinDescriptor.padName); - - if (!pad.gpioPinNumber.has_value()) { - continue; - } - - auto pinState = TargetPinState(); - - if (pad.ddrSetAddress.has_value()) { - auto dataDirectionRegisterValue = readMemoryBitset(pad.ddrSetAddress.value()); - pinState.ioDirection = dataDirectionRegisterValue.test(pad.gpioPinNumber.value()) ? - TargetPinState::IoDirection::OUTPUT : TargetPinState::IoDirection::INPUT; - - if (pinState.ioDirection == TargetPinState::IoDirection::OUTPUT && pad.gpioPortSetAddress.has_value()) { - auto portRegisterValue = readMemoryBitset(pad.gpioPortSetAddress.value()); - pinState.ioState = portRegisterValue.test(pad.gpioPinNumber.value()) ? - TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW; - - } else if (pinState.ioDirection == TargetPinState::IoDirection::INPUT - && pad.gpioPortInputAddress.has_value() - ) { - auto portInputRegisterValue = readMemoryBitset(pad.gpioPortInputAddress.value()); - auto h = portInputRegisterValue.to_string(); - pinState.ioState = portInputRegisterValue.test(pad.gpioPinNumber.value()) ? - TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW; - } - } - - output.insert(std::pair(pinNumber, pinState)); - } + this->avr8Interface->configure(targetConfig); } - return output; -} - -void Avr8::setPinState(const TargetPinDescriptor& pinDescriptor, const TargetPinState& state) { - auto variantId = pinDescriptor.variantId; - if (!this->targetVariantsById.contains(variantId)) { - throw Exception("Invalid target variant ID"); - } - - if (!this->padDescriptorsByName.contains(pinDescriptor.padName)) { - throw Exception("Unknown pad"); - } - - if (!state.ioDirection.has_value()) { - throw Exception("Missing IO direction state"); - } - - auto& variant = this->targetVariantsById.at(variantId); - auto& padDescriptor = this->padDescriptorsByName.at(pinDescriptor.padName); - auto ioState = state.ioState; - - if (state.ioDirection == TargetPinState::IoDirection::INPUT) { - // When setting the direction to INPUT, we must always set the IO pinstate to LOW - ioState = TargetPinState::IoState::LOW; - } - - if (!padDescriptor.ddrSetAddress.has_value() - || !padDescriptor.gpioPortSetAddress.has_value() - || !padDescriptor.gpioPinNumber.has_value() - ) { - throw Exception("Inadequate pad descriptor"); - } - - auto pinNumber = padDescriptor.gpioPinNumber.value(); - auto ddrSetAddress = padDescriptor.ddrSetAddress.value(); - auto ddrSetValue = this->readMemory(TargetMemoryType::RAM, ddrSetAddress, 1); - - if (ddrSetValue.empty()) { - throw Exception("Failed to read DDSR value"); - } - - auto ddrSetBitset = std::bitset::digits>(ddrSetValue.front()); - if (ddrSetBitset.test(pinNumber) != (state.ioDirection == TargetPinState::IoDirection::OUTPUT)) { - // DDR needs updating - ddrSetBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::OUTPUT)); - - this->writeMemory( - TargetMemoryType::RAM, - ddrSetAddress, - {static_cast(ddrSetBitset.to_ulong())} - ); - } - - if (padDescriptor.ddrClearAddress.has_value() && padDescriptor.ddrClearAddress != ddrSetAddress) { - // We also need to ensure the data direction clear register value is correct - auto ddrClearAddress = padDescriptor.ddrClearAddress.value(); - auto ddrClearValue = this->readMemory(TargetMemoryType::RAM, ddrClearAddress, 1); - - if (ddrClearValue.empty()) { - throw Exception("Failed to read DDCR value"); - } - - auto ddrClearBitset = std::bitset::digits>(ddrClearValue.front()); - if (ddrClearBitset.test(pinNumber) == (state.ioDirection == TargetPinState::IoDirection::INPUT)) { - ddrClearBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::INPUT)); - - this->writeMemory( - TargetMemoryType::RAM, - ddrClearAddress, - {static_cast(ddrClearBitset.to_ulong())} - ); - } - } - - if (ioState.has_value()) { - auto portSetAddress = padDescriptor.gpioPortSetAddress.value(); - - if (ioState == TargetPinState::IoState::HIGH - || !padDescriptor.gpioPortClearAddress.has_value() - || padDescriptor.gpioPortClearAddress == portSetAddress - ) { - if (padDescriptor.gpioPortClearAddress != portSetAddress) { - /* - * We don't need to read the SET register if the SET and CLEAR operations are performed via different - * registers. - * - * Instead, we can just set the appropriate bit against the SET register. - */ - this->writeMemory( - TargetMemoryType::RAM, - portSetAddress, - {static_cast(0x01 << pinNumber)} - ); - - } else { - auto portSetRegisterValue = this->readMemory(TargetMemoryType::RAM, portSetAddress, 1); - - if (portSetRegisterValue.empty()) { - throw Exception("Failed to read PORT register value"); - } - - auto portSetBitset = std::bitset::digits>(portSetRegisterValue.front()); - if (portSetBitset.test(pinNumber) != (ioState == TargetPinState::IoState::HIGH)) { - // PORT set register needs updating - portSetBitset.set(pinNumber, (ioState == TargetPinState::IoState::HIGH)); - - this->writeMemory( - TargetMemoryType::RAM, - portSetAddress, - {static_cast(portSetBitset.to_ulong())} - ); - } - } + void Avr8::postActivationConfigure() { + if (!this->targetDescriptionFile.has_value()) { + this->loadTargetDescriptionFile(); + this->initFromTargetDescriptionFile(); } /* - * We only need to update the PORT clear register if the IO state was set to LOW, and the clear register is - * not the same register as the set register. + * The signature obtained from the device should match what is in the target description file + * + * We don't use this->getId() here as that could return the ID that was extracted from the target description + * file (which it would, if the user specified the exact target name in their project config - see + * Avr8::getId() and TargetController::getSupportedTargets() for more). */ - if (ioState == TargetPinState::IoState::LOW - && padDescriptor.gpioPortClearAddress.has_value() - && padDescriptor.gpioPortClearAddress != portSetAddress - ) { - // We also need to ensure the PORT clear register value is correct - auto portClearAddress = padDescriptor.gpioPortClearAddress.value(); + auto targetSignature = this->avr8Interface->getDeviceId(); + auto tdSignature = this->targetDescriptionFile->getTargetSignature(); - this->writeMemory( - TargetMemoryType::RAM, - portClearAddress, - {static_cast(0x01 << pinNumber)} + if (targetSignature != tdSignature) { + throw Exception( + "Failed to validate connected target - target signature mismatch.\nThe target signature" + " (\"" + targetSignature.toHex() + "\") does not match the AVR8 target description signature (\"" + + tdSignature.toHex() + "\"). This will likely be due to an incorrect target name in the configuration" + + " file (bloom.json)." ); } } -} -void Avr8::loadTargetDescriptionFile() { - this->targetDescriptionFile = TargetDescription::TargetDescriptionFile( - this->getId(), - (!this->name.empty()) ? std::optional(this->name) : std::nullopt - ); -} + void Avr8::postPromotionConfigure() { + if (!this->family.has_value()) { + throw Exception("Failed to resolve AVR8 family"); + } -void Avr8::initFromTargetDescriptionFile() { - assert(this->targetDescriptionFile.has_value()); - this->name = this->targetDescriptionFile->getTargetName(); - this->family = this->targetDescriptionFile->getFamily(); - - this->targetParameters = this->targetDescriptionFile->getTargetParameters(); - this->padDescriptorsByName = this->targetDescriptionFile->getPadDescriptorsMappedByName(); - this->targetVariantsById = this->targetDescriptionFile->getVariantsMappedById(); - - if (!this->targetParameters->stackPointerRegisterLowAddress.has_value()) { - throw Exception("Failed to load sufficient AVR8 target paramters - missting stack pointer start address"); + this->avr8Interface->setFamily(this->family.value()); + this->avr8Interface->setTargetParameters(this->targetParameters.value()); } - if (!this->targetParameters->statusRegisterStartAddress.has_value()) { - throw Exception("Failed to load sufficient AVR8 target parameters - missting status register start address"); + void Avr8::activate() { + if (this->isActivated()) { + return; + } + + this->avr8Interface->init(); + + if (this->targetDescriptionFile.has_value()) { + this->avr8Interface->setTargetParameters(this->targetParameters.value()); + } + + this->avr8Interface->activate(); + this->activated = true; } - this->loadTargetRegisterDescriptors(); - this->loadTargetMemoryDescriptors(); -} + void Avr8::deactivate() { + try { + this->avr8Interface->deactivate(); + this->activated = false; -void Avr8::loadTargetRegisterDescriptors() { - this->targetRegisterDescriptorsByType = this->targetDescriptionFile->getRegisterDescriptorsMappedByType(); + } catch (const Exception& exception) { + Logger::error("Failed to deactivate AVR8 target - " + exception.getMessage()); + } + } - /* - * All AVR8 targets possess 32 general purpose CPU registers. These are not described in the TDF, so we - * construct the descriptors for them here. - */ - auto gpRegisterStartAddress = this->targetParameters->gpRegisterStartAddress.value_or(0); - for (std::uint8_t i = 0; i <= 31; i++) { - auto generalPurposeRegisterDescriptor = TargetRegisterDescriptor(); - generalPurposeRegisterDescriptor.startAddress = gpRegisterStartAddress + i; - generalPurposeRegisterDescriptor.size = 1; - generalPurposeRegisterDescriptor.type = TargetRegisterType::GENERAL_PURPOSE_REGISTER; - generalPurposeRegisterDescriptor.name = "r" + std::to_string(i); - generalPurposeRegisterDescriptor.groupName = "general purpose cpu"; - generalPurposeRegisterDescriptor.readable = true; - generalPurposeRegisterDescriptor.writable = true; + std::unique_ptr Avr8::promote() { + std::unique_ptr promoted = nullptr; - this->targetRegisterDescriptorsByType[generalPurposeRegisterDescriptor.type].insert( - generalPurposeRegisterDescriptor + if (this->family.has_value()) { + // Promote generic AVR8 target to correct family. + switch (this->family.value()) { + case Family::XMEGA: { + Logger::info("AVR8 target promoted to XMega target"); + promoted = std::make_unique(*this); + break; + } + case Family::MEGA: { + Logger::info("AVR8 target promoted to megaAVR target"); + promoted = std::make_unique(*this); + break; + } + case Family::TINY: { + Logger::info("AVR8 target promoted to tinyAVR target"); + promoted = std::make_unique(*this); + break; + } + default: { + break; + } + } + } + + return promoted; + } + + TargetDescriptor Avr8Bit::Avr8::getDescriptor() { + auto descriptor = TargetDescriptor(); + descriptor.id = this->getHumanReadableId(); + descriptor.name = this->getName(); + descriptor.registerDescriptorsByType = this->targetRegisterDescriptorsByType; + descriptor.memoryDescriptorsByType = this->targetMemoryDescriptorsByType; + + std::transform( + this->targetVariantsById.begin(), + this->targetVariantsById.end(), + std::back_inserter(descriptor.variants), + [] (auto& variantToIdPair) { + return variantToIdPair.second; + } + ); + + return descriptor; + } + + void Avr8::run() { + this->avr8Interface->run(); + } + + void Avr8::stop() { + this->avr8Interface->stop(); + } + + void Avr8::step() { + this->avr8Interface->step(); + } + + void Avr8::reset() { + this->avr8Interface->reset(); + } + + void Avr8::setBreakpoint(std::uint32_t address) { + this->avr8Interface->setBreakpoint(address); + } + + void Avr8::removeBreakpoint(std::uint32_t address) { + this->avr8Interface->clearBreakpoint(address); + } + + void Avr8::clearAllBreakpoints() { + this->avr8Interface->clearAllBreakpoints(); + } + + void Avr8::writeRegisters(TargetRegisters registers) { + for (auto registerIt = registers.begin(); registerIt != registers.end();) { + if (registerIt->descriptor.type == TargetRegisterType::PROGRAM_COUNTER) { + auto programCounterBytes = registerIt->value; + + if (programCounterBytes.size() < 4) { + // All PC register values should be at least 4 bytes in size + programCounterBytes.insert(programCounterBytes.begin(), 4 - programCounterBytes.size(), 0x00); + } + + this->setProgramCounter(static_cast( + programCounterBytes[0] << 24 + | programCounterBytes[1] << 16 + | programCounterBytes[2] << 8 + | programCounterBytes[3] + )); + + registerIt = registers.erase(registerIt); + + } else { + registerIt++; + } + } + + if (!registers.empty()) { + this->avr8Interface->writeRegisters(registers); + } + } + + TargetRegisters Avr8::readRegisters(TargetRegisterDescriptors descriptors) { + TargetRegisters registers; + + for (auto registerDescriptorIt = descriptors.begin(); registerDescriptorIt != descriptors.end();) { + const auto& descriptor = *registerDescriptorIt; + + if (descriptor.type == TargetRegisterType::PROGRAM_COUNTER) { + registers.push_back(this->getProgramCounterRegister()); + + registerDescriptorIt = descriptors.erase(registerDescriptorIt); + + } else { + registerDescriptorIt++; + } + } + + if (!descriptors.empty()) { + auto otherRegisters = this->avr8Interface->readRegisters(descriptors); + registers.insert(registers.end(), otherRegisters.begin(), otherRegisters.end()); + } + + return registers; + } + + TargetMemoryBuffer Avr8::readMemory( + TargetMemoryType memoryType, + std::uint32_t startAddress, + std::uint32_t bytes, + const std::set& excludedAddressRanges + ) { + return this->avr8Interface->readMemory(memoryType, startAddress, bytes, excludedAddressRanges); + } + + void Avr8::writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) { + this->avr8Interface->writeMemory(memoryType, startAddress, buffer); + } + + TargetState Avr8::getState() { + return this->avr8Interface->getTargetState(); + } + + std::uint32_t Avr8::getProgramCounter() { + return this->avr8Interface->getProgramCounter(); + } + + TargetRegister Avr8::getProgramCounterRegister() { + auto programCounter = this->getProgramCounter(); + + return TargetRegister(TargetRegisterDescriptor(TargetRegisterType::PROGRAM_COUNTER), { + static_cast(programCounter >> 24), + static_cast(programCounter >> 16), + static_cast(programCounter >> 8), + static_cast(programCounter), + }); + } + + void Avr8::setProgramCounter(std::uint32_t programCounter) { + this->avr8Interface->setProgramCounter(programCounter); + } + + std::uint32_t Avr8::getStackPointer() { + const auto stackPointerRegister = this->readRegisters( + {this->targetRegisterDescriptorsByType.at(TargetRegisterType::STACK_POINTER)} + ).front(); + + std::uint32_t stackPointer = 0; + for (std::size_t i = 0; i < stackPointerRegister.size() && i < 4; i++) { + stackPointer = (stackPointer << (8 * i)) | stackPointerRegister.value[i]; + } + + return stackPointer; + } + + std::map Avr8::getPinStates(int variantId) { + if (!this->targetVariantsById.contains(variantId)) { + throw Exception("Invalid target variant ID"); + } + + std::map output; + auto& variant = this->targetVariantsById.at(variantId); + + /* + * To prevent the number of memory reads we perform here, we cache the data and map it by start address. + * + * This way, we only perform 3 memory reads for a target variant with 3 ports - one per port (instead of one + * per pin). + * + * We may be able to make this more efficient by combining reads for ports with aligned memory addresses. This + * will be considered when the need for it becomes apparent. + */ + std::map cachedMemoryByStartAddress; + auto readMemoryBitset = [this, &cachedMemoryByStartAddress] (std::uint16_t startAddress) { + if (!cachedMemoryByStartAddress.contains(startAddress)) { + cachedMemoryByStartAddress.insert( + std::pair( + startAddress, + this->readMemory(TargetMemoryType::RAM, startAddress, 1) + ) + ); + } + + return std::bitset::digits>( + cachedMemoryByStartAddress.at(startAddress).at(0) + ); + }; + + for (const auto& [pinNumber, pinDescriptor] : variant.pinDescriptorsByNumber) { + if (this->padDescriptorsByName.contains(pinDescriptor.padName)) { + auto& pad = this->padDescriptorsByName.at(pinDescriptor.padName); + + if (!pad.gpioPinNumber.has_value()) { + continue; + } + + auto pinState = TargetPinState(); + + if (pad.ddrSetAddress.has_value()) { + auto dataDirectionRegisterValue = readMemoryBitset(pad.ddrSetAddress.value()); + pinState.ioDirection = dataDirectionRegisterValue.test(pad.gpioPinNumber.value()) ? + TargetPinState::IoDirection::OUTPUT : TargetPinState::IoDirection::INPUT; + + if (pinState.ioDirection == TargetPinState::IoDirection::OUTPUT + && pad.gpioPortSetAddress.has_value() + ) { + auto portRegisterValue = readMemoryBitset(pad.gpioPortSetAddress.value()); + pinState.ioState = portRegisterValue.test(pad.gpioPinNumber.value()) ? + TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW; + + } else if (pinState.ioDirection == TargetPinState::IoDirection::INPUT + && pad.gpioPortInputAddress.has_value() + ) { + auto portInputRegisterValue = readMemoryBitset(pad.gpioPortInputAddress.value()); + auto h = portInputRegisterValue.to_string(); + pinState.ioState = portInputRegisterValue.test(pad.gpioPinNumber.value()) ? + TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW; + } + } + + output.insert(std::pair(pinNumber, pinState)); + } + } + + return output; + } + + void Avr8::setPinState(const TargetPinDescriptor& pinDescriptor, const TargetPinState& state) { + auto variantId = pinDescriptor.variantId; + if (!this->targetVariantsById.contains(variantId)) { + throw Exception("Invalid target variant ID"); + } + + if (!this->padDescriptorsByName.contains(pinDescriptor.padName)) { + throw Exception("Unknown pad"); + } + + if (!state.ioDirection.has_value()) { + throw Exception("Missing IO direction state"); + } + + auto& variant = this->targetVariantsById.at(variantId); + auto& padDescriptor = this->padDescriptorsByName.at(pinDescriptor.padName); + auto ioState = state.ioState; + + if (state.ioDirection == TargetPinState::IoDirection::INPUT) { + // When setting the direction to INPUT, we must always set the IO pinstate to LOW + ioState = TargetPinState::IoState::LOW; + } + + if (!padDescriptor.ddrSetAddress.has_value() + || !padDescriptor.gpioPortSetAddress.has_value() + || !padDescriptor.gpioPinNumber.has_value() + ) { + throw Exception("Inadequate pad descriptor"); + } + + auto pinNumber = padDescriptor.gpioPinNumber.value(); + auto ddrSetAddress = padDescriptor.ddrSetAddress.value(); + auto ddrSetValue = this->readMemory(TargetMemoryType::RAM, ddrSetAddress, 1); + + if (ddrSetValue.empty()) { + throw Exception("Failed to read DDSR value"); + } + + auto ddrSetBitset = std::bitset::digits>(ddrSetValue.front()); + if (ddrSetBitset.test(pinNumber) != (state.ioDirection == TargetPinState::IoDirection::OUTPUT)) { + // DDR needs updating + ddrSetBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::OUTPUT)); + + this->writeMemory( + TargetMemoryType::RAM, + ddrSetAddress, + {static_cast(ddrSetBitset.to_ulong())} + ); + } + + if (padDescriptor.ddrClearAddress.has_value() && padDescriptor.ddrClearAddress != ddrSetAddress) { + // We also need to ensure the data direction clear register value is correct + auto ddrClearAddress = padDescriptor.ddrClearAddress.value(); + auto ddrClearValue = this->readMemory(TargetMemoryType::RAM, ddrClearAddress, 1); + + if (ddrClearValue.empty()) { + throw Exception("Failed to read DDCR value"); + } + + auto ddrClearBitset = std::bitset::digits>(ddrClearValue.front()); + if (ddrClearBitset.test(pinNumber) == (state.ioDirection == TargetPinState::IoDirection::INPUT)) { + ddrClearBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::INPUT)); + + this->writeMemory( + TargetMemoryType::RAM, + ddrClearAddress, + {static_cast(ddrClearBitset.to_ulong())} + ); + } + } + + if (ioState.has_value()) { + auto portSetAddress = padDescriptor.gpioPortSetAddress.value(); + + if (ioState == TargetPinState::IoState::HIGH + || !padDescriptor.gpioPortClearAddress.has_value() + || padDescriptor.gpioPortClearAddress == portSetAddress + ) { + if (padDescriptor.gpioPortClearAddress != portSetAddress) { + /* + * We don't need to read the SET register if the SET and CLEAR operations are performed via + * different registers. + * + * Instead, we can just set the appropriate bit against the SET register. + */ + this->writeMemory( + TargetMemoryType::RAM, + portSetAddress, + {static_cast(0x01 << pinNumber)} + ); + + } else { + auto portSetRegisterValue = this->readMemory( + TargetMemoryType::RAM, + portSetAddress, + 1 + ); + + if (portSetRegisterValue.empty()) { + throw Exception("Failed to read PORT register value"); + } + + auto portSetBitset = std::bitset::digits>( + portSetRegisterValue.front() + ); + if (portSetBitset.test(pinNumber) != (ioState == TargetPinState::IoState::HIGH)) { + // PORT set register needs updating + portSetBitset.set(pinNumber, (ioState == TargetPinState::IoState::HIGH)); + + this->writeMemory( + TargetMemoryType::RAM, + portSetAddress, + {static_cast(portSetBitset.to_ulong())} + ); + } + } + } + + /* + * We only need to update the PORT clear register if the IO state was set to LOW, and the clear register is + * not the same register as the set register. + */ + if (ioState == TargetPinState::IoState::LOW + && padDescriptor.gpioPortClearAddress.has_value() + && padDescriptor.gpioPortClearAddress != portSetAddress + ) { + // We also need to ensure the PORT clear register value is correct + auto portClearAddress = padDescriptor.gpioPortClearAddress.value(); + + this->writeMemory( + TargetMemoryType::RAM, + portClearAddress, + {static_cast(0x01 << pinNumber)} + ); + } + } + } + + void Avr8::loadTargetDescriptionFile() { + this->targetDescriptionFile = TargetDescription::TargetDescriptionFile( + this->getId(), + (!this->name.empty()) ? std::optional(this->name) : std::nullopt ); } - /* - * The SP and SREG registers are described in the TDF, so we could just use the descriptors extracted from the - * TDF. The problem with that is, sometimes the SP register consists of two bytes; an SPL and an SPH. These need - * to be combined into one register descriptor. This is why we just use what we already have in - * this->targetParameters. - */ - auto stackPointerRegisterDescriptor = TargetRegisterDescriptor(); - stackPointerRegisterDescriptor.type = TargetRegisterType::STACK_POINTER; - stackPointerRegisterDescriptor.startAddress = this->targetParameters->stackPointerRegisterLowAddress.value(); - stackPointerRegisterDescriptor.size = this->targetParameters->stackPointerRegisterSize.value(); - stackPointerRegisterDescriptor.name = "SP"; - stackPointerRegisterDescriptor.groupName = "CPU"; - stackPointerRegisterDescriptor.description = "Stack Pointer Register"; - stackPointerRegisterDescriptor.readable = true; - stackPointerRegisterDescriptor.writable = true; + void Avr8::initFromTargetDescriptionFile() { + assert(this->targetDescriptionFile.has_value()); + this->name = this->targetDescriptionFile->getTargetName(); + this->family = this->targetDescriptionFile->getFamily(); - auto statusRegisterDescriptor = TargetRegisterDescriptor(); - statusRegisterDescriptor.type = TargetRegisterType::STATUS_REGISTER; - statusRegisterDescriptor.startAddress = this->targetParameters->statusRegisterStartAddress.value(); - statusRegisterDescriptor.size = this->targetParameters->statusRegisterSize.value(); - statusRegisterDescriptor.name = "SREG"; - statusRegisterDescriptor.groupName = "CPU"; - statusRegisterDescriptor.description = "Status Register"; - statusRegisterDescriptor.readable = true; - statusRegisterDescriptor.writable = true; + this->targetParameters = this->targetDescriptionFile->getTargetParameters(); + this->padDescriptorsByName = this->targetDescriptionFile->getPadDescriptorsMappedByName(); + this->targetVariantsById = this->targetDescriptionFile->getVariantsMappedById(); - auto programCounterRegisterDescriptor = TargetRegisterDescriptor(); - programCounterRegisterDescriptor.type = TargetRegisterType::PROGRAM_COUNTER; - programCounterRegisterDescriptor.size = 4; - programCounterRegisterDescriptor.name = "PC"; - programCounterRegisterDescriptor.groupName = "CPU"; - programCounterRegisterDescriptor.description = "Program Counter"; - programCounterRegisterDescriptor.readable = true; - programCounterRegisterDescriptor.writable = true; + if (!this->targetParameters->stackPointerRegisterLowAddress.has_value()) { + throw Exception( + "Failed to load sufficient AVR8 target paramters - missting stack pointer start address" + ); + } - this->targetRegisterDescriptorsByType[stackPointerRegisterDescriptor.type].insert( - stackPointerRegisterDescriptor - ); - this->targetRegisterDescriptorsByType[statusRegisterDescriptor.type].insert( - statusRegisterDescriptor - ); - this->targetRegisterDescriptorsByType[programCounterRegisterDescriptor.type].insert( - programCounterRegisterDescriptor - ); -} + if (!this->targetParameters->statusRegisterStartAddress.has_value()) { + throw Exception( + "Failed to load sufficient AVR8 target parameters - missting status register start address" + ); + } -void Avr8::loadTargetMemoryDescriptors() { - const auto ramStartAddress = this->targetParameters->ramStartAddress.value(); + this->loadTargetRegisterDescriptors(); + this->loadTargetMemoryDescriptors(); + } - this->targetMemoryDescriptorsByType.insert(std::pair( - TargetMemoryType::RAM, - TargetMemoryDescriptor( - TargetMemoryType::RAM, - TargetMemoryAddressRange( - ramStartAddress, - ramStartAddress + this->targetParameters->ramSize.value() - 1 - ) - ) - )); + void Avr8::loadTargetRegisterDescriptors() { + this->targetRegisterDescriptorsByType = this->targetDescriptionFile->getRegisterDescriptorsMappedByType(); - if (this->targetParameters->eepromStartAddress.has_value() && this->targetParameters->eepromSize.has_value()) { - const auto eepromStartAddress = this->targetParameters->eepromStartAddress.value(); + /* + * All AVR8 targets possess 32 general purpose CPU registers. These are not described in the TDF, so we + * construct the descriptors for them here. + */ + auto gpRegisterStartAddress = this->targetParameters->gpRegisterStartAddress.value_or(0); + for (std::uint8_t i = 0; i <= 31; i++) { + auto generalPurposeRegisterDescriptor = TargetRegisterDescriptor(); + generalPurposeRegisterDescriptor.startAddress = gpRegisterStartAddress + i; + generalPurposeRegisterDescriptor.size = 1; + generalPurposeRegisterDescriptor.type = TargetRegisterType::GENERAL_PURPOSE_REGISTER; + generalPurposeRegisterDescriptor.name = "r" + std::to_string(i); + generalPurposeRegisterDescriptor.groupName = "general purpose cpu"; + generalPurposeRegisterDescriptor.readable = true; + generalPurposeRegisterDescriptor.writable = true; + + this->targetRegisterDescriptorsByType[generalPurposeRegisterDescriptor.type].insert( + generalPurposeRegisterDescriptor + ); + } + + /* + * The SP and SREG registers are described in the TDF, so we could just use the descriptors extracted from the + * TDF. The problem with that is, sometimes the SP register consists of two bytes; an SPL and an SPH. These need + * to be combined into one register descriptor. This is why we just use what we already have in + * this->targetParameters. + */ + auto stackPointerRegisterDescriptor = TargetRegisterDescriptor(); + stackPointerRegisterDescriptor.type = TargetRegisterType::STACK_POINTER; + stackPointerRegisterDescriptor.startAddress = this->targetParameters->stackPointerRegisterLowAddress.value(); + stackPointerRegisterDescriptor.size = this->targetParameters->stackPointerRegisterSize.value(); + stackPointerRegisterDescriptor.name = "SP"; + stackPointerRegisterDescriptor.groupName = "CPU"; + stackPointerRegisterDescriptor.description = "Stack Pointer Register"; + stackPointerRegisterDescriptor.readable = true; + stackPointerRegisterDescriptor.writable = true; + + auto statusRegisterDescriptor = TargetRegisterDescriptor(); + statusRegisterDescriptor.type = TargetRegisterType::STATUS_REGISTER; + statusRegisterDescriptor.startAddress = this->targetParameters->statusRegisterStartAddress.value(); + statusRegisterDescriptor.size = this->targetParameters->statusRegisterSize.value(); + statusRegisterDescriptor.name = "SREG"; + statusRegisterDescriptor.groupName = "CPU"; + statusRegisterDescriptor.description = "Status Register"; + statusRegisterDescriptor.readable = true; + statusRegisterDescriptor.writable = true; + + auto programCounterRegisterDescriptor = TargetRegisterDescriptor(); + programCounterRegisterDescriptor.type = TargetRegisterType::PROGRAM_COUNTER; + programCounterRegisterDescriptor.size = 4; + programCounterRegisterDescriptor.name = "PC"; + programCounterRegisterDescriptor.groupName = "CPU"; + programCounterRegisterDescriptor.description = "Program Counter"; + programCounterRegisterDescriptor.readable = true; + programCounterRegisterDescriptor.writable = true; + + this->targetRegisterDescriptorsByType[stackPointerRegisterDescriptor.type].insert( + stackPointerRegisterDescriptor + ); + this->targetRegisterDescriptorsByType[statusRegisterDescriptor.type].insert( + statusRegisterDescriptor + ); + this->targetRegisterDescriptorsByType[programCounterRegisterDescriptor.type].insert( + programCounterRegisterDescriptor + ); + } + + void Avr8::loadTargetMemoryDescriptors() { + const auto ramStartAddress = this->targetParameters->ramStartAddress.value(); this->targetMemoryDescriptorsByType.insert(std::pair( - TargetMemoryType::EEPROM, + TargetMemoryType::RAM, TargetMemoryDescriptor( - TargetMemoryType::EEPROM, + TargetMemoryType::RAM, TargetMemoryAddressRange( - eepromStartAddress, - eepromStartAddress + this->targetParameters->eepromSize.value() - 1 + ramStartAddress, + ramStartAddress + this->targetParameters->ramSize.value() - 1 ) ) )); - } -} -TargetSignature Avr8::getId() { - if (!this->id.has_value()) { - this->id = this->avr8Interface->getDeviceId(); + if (this->targetParameters->eepromStartAddress.has_value() && this->targetParameters->eepromSize.has_value()) { + const auto eepromStartAddress = this->targetParameters->eepromStartAddress.value(); + + this->targetMemoryDescriptorsByType.insert(std::pair( + TargetMemoryType::EEPROM, + TargetMemoryDescriptor( + TargetMemoryType::EEPROM, + TargetMemoryAddressRange( + eepromStartAddress, + eepromStartAddress + this->targetParameters->eepromSize.value() - 1 + ) + ) + )); + } } - return this->id.value(); + TargetSignature Avr8::getId() { + if (!this->id.has_value()) { + this->id = this->avr8Interface->getDeviceId(); + } + + return this->id.value(); + } } diff --git a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp index 8b49f480..e61df91c 100644 --- a/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp @@ -9,1122 +9,1133 @@ #include "src/Exceptions/Exception.hpp" #include "src/Targets/TargetDescription/Exceptions/TargetDescriptionParsingFailureException.hpp" -using namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription; -using namespace Bloom::Targets::Microchip::Avr::Avr8Bit; -using namespace Bloom::Targets::Microchip::Avr; -using namespace Bloom::Exceptions; +namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription +{ + using namespace Bloom::Exceptions; -using Bloom::Targets::TargetDescription::RegisterGroup; -using Bloom::Targets::TargetDescription::MemorySegment; -using Bloom::Targets::TargetDescription::MemorySegmentType; -using Bloom::Targets::TargetDescription::Register; -using Bloom::Targets::TargetVariant; -using Bloom::Targets::TargetRegisterDescriptor; + using Bloom::Targets::TargetDescription::RegisterGroup; + using Bloom::Targets::TargetDescription::MemorySegment; + using Bloom::Targets::TargetDescription::MemorySegmentType; + using Bloom::Targets::TargetDescription::Register; + using Bloom::Targets::TargetVariant; + using Bloom::Targets::TargetRegisterDescriptor; -TargetDescriptionFile::TargetDescriptionFile( - const TargetSignature& targetSignature, - std::optional targetName -) { - auto targetSignatureHex = targetSignature.toHex(); - auto mapping = TargetDescriptionFile::getTargetDescriptionMapping(); - auto qTargetSignatureHex = QString::fromStdString(targetSignatureHex).toLower(); + TargetDescriptionFile::TargetDescriptionFile( + const TargetSignature& targetSignature, + std::optional targetName + ) { + auto targetSignatureHex = targetSignature.toHex(); + auto mapping = TargetDescriptionFile::getTargetDescriptionMapping(); + auto qTargetSignatureHex = QString::fromStdString(targetSignatureHex).toLower(); - if (mapping.contains(qTargetSignatureHex)) { - // We have a match for the target signature. - auto descriptionFilesJsonArray = mapping.find(qTargetSignatureHex).value().toArray(); - auto matchingDescriptionFiles = std::vector(); - std::copy_if( - descriptionFilesJsonArray.begin(), - descriptionFilesJsonArray.end(), - std::back_inserter(matchingDescriptionFiles), - [&targetName] (const QJsonValue& value) { - auto pdTargetName = value.toObject().find("targetName")->toString().toLower().toStdString(); - return !targetName.has_value() || (targetName.has_value() && targetName.value() == pdTargetName); - } - ); - - if (targetName.has_value() && matchingDescriptionFiles.empty()) { - throw Exception("Failed to resolve target description file for target \"" + targetName.value() - + "\" - target signature \"" + targetSignatureHex + "\" does not belong to target with name \"" + - targetName.value() + "\". Please review your bloom.json configuration."); - } - - if (matchingDescriptionFiles.size() == 1) { - // Attempt to load the XML target description file - auto descriptionFilePath = QString::fromStdString(Paths::applicationDirPath()) + "/" - + matchingDescriptionFiles.front().toObject().find("targetDescriptionFilePath")->toString(); - - Logger::debug("Loading AVR8 target description file: " + descriptionFilePath.toStdString()); - Targets::TargetDescription::TargetDescriptionFile::init(descriptionFilePath); - - } else if (matchingDescriptionFiles.size() > 1) { - /* - * There are numerous target description files mapped to this target signature. There's really not - * much we can do at this point, so we'll just instruct the user to use a more specific target name. - */ - QStringList targetNames; - std::transform( - matchingDescriptionFiles.begin(), - matchingDescriptionFiles.end(), - std::back_inserter(targetNames), - [] (const QJsonValue& descriptionFile) { - return QString("\"" + descriptionFile.toObject().find("targetName")->toString().toLower() + "\""); + if (mapping.contains(qTargetSignatureHex)) { + // We have a match for the target signature. + auto descriptionFilesJsonArray = mapping.find(qTargetSignatureHex).value().toArray(); + auto matchingDescriptionFiles = std::vector(); + std::copy_if( + descriptionFilesJsonArray.begin(), + descriptionFilesJsonArray.end(), + std::back_inserter(matchingDescriptionFiles), + [&targetName] (const QJsonValue& value) { + auto pdTargetName = value.toObject().find("targetName")->toString().toLower().toStdString(); + return !targetName.has_value() || (targetName.has_value() && targetName.value() == pdTargetName); } ); - throw Exception("Failed to resolve target description file for target \"" - + targetSignatureHex + "\" - ambiguous signature.\nThe signature is mapped to numerous targets: " - + targetNames.join(", ").toStdString() + ".\n\nPlease update the target name in your Bloom " + - "configuration to one of the above." - ); + if (targetName.has_value() && matchingDescriptionFiles.empty()) { + throw Exception("Failed to resolve target description file for target \"" + targetName.value() + + "\" - target signature \"" + targetSignatureHex + "\" does not belong to target with name \"" + + targetName.value() + "\". Please review your bloom.json configuration."); + } + + if (matchingDescriptionFiles.size() == 1) { + // Attempt to load the XML target description file + auto descriptionFilePath = QString::fromStdString(Paths::applicationDirPath()) + "/" + + matchingDescriptionFiles.front().toObject().find("targetDescriptionFilePath")->toString(); + + Logger::debug("Loading AVR8 target description file: " + descriptionFilePath.toStdString()); + Targets::TargetDescription::TargetDescriptionFile::init(descriptionFilePath); + + } else if (matchingDescriptionFiles.size() > 1) { + /* + * There are numerous target description files mapped to this target signature. There's really not + * much we can do at this point, so we'll just instruct the user to use a more specific target name. + */ + QStringList targetNames; + std::transform( + matchingDescriptionFiles.begin(), + matchingDescriptionFiles.end(), + std::back_inserter(targetNames), + [] (const QJsonValue& descriptionFile) { + return QString( + "\"" + descriptionFile.toObject().find("targetName")->toString().toLower() + "\"" + ); + } + ); + + throw Exception("Failed to resolve target description file for target \"" + + targetSignatureHex + "\" - ambiguous signature.\nThe signature is mapped to numerous targets: " + + targetNames.join(", ").toStdString() + ".\n\nPlease update the target name in your Bloom " + + "configuration to one of the above." + ); + + } else { + throw Exception("Failed to resolve target description file for target \"" + + targetSignatureHex + "\" - invalid AVR8 target description mapping." + ); + } } else { throw Exception("Failed to resolve target description file for target \"" - + targetSignatureHex + "\" - invalid AVR8 target description mapping." + + targetSignatureHex + "\" - unknown target signature."); + } + } + + void TargetDescriptionFile::init(const QDomDocument& xml) { + Targets::TargetDescription::TargetDescriptionFile::init(xml); + + this->loadDebugPhysicalInterfaces(); + this->loadPadDescriptors(); + this->loadTargetVariants(); + this->loadTargetRegisterDescriptors(); + } + + QJsonObject TargetDescriptionFile::getTargetDescriptionMapping() { + auto mappingFile = QFile( + QString::fromStdString(Paths::resourcesDirPath() + "/TargetDescriptionFiles/AVR/Mapping.json") + ); + + if (!mappingFile.exists()) { + throw Exception("Failed to load AVR target description mapping - mapping file not found"); + } + + mappingFile.open(QIODevice::ReadOnly); + return QJsonDocument::fromJson(mappingFile.readAll()).object(); + } + + TargetSignature TargetDescriptionFile::getTargetSignature() const { + auto& propertyGroups = this->propertyGroupsMappedByName; + auto signaturePropertyGroupIt = propertyGroups.find("signatures"); + + if (signaturePropertyGroupIt == propertyGroups.end()) { + throw TargetDescriptionParsingFailureException("Signature property group not found"); + } + + auto signaturePropertyGroup = signaturePropertyGroupIt->second; + auto& signatureProperties = signaturePropertyGroup.propertiesMappedByName; + std::optional signatureByteZero; + std::optional signatureByteOne; + std::optional signatureByteTwo; + + if (signatureProperties.contains("signature0")) { + signatureByteZero = static_cast( + signatureProperties.at("signature0").value.toShort(nullptr, 16) ); } - } else { - throw Exception("Failed to resolve target description file for target \"" - + targetSignatureHex + "\" - unknown target signature."); - } -} - -void TargetDescriptionFile::init(const QDomDocument& xml) { - Targets::TargetDescription::TargetDescriptionFile::init(xml); - - this->loadDebugPhysicalInterfaces(); - this->loadPadDescriptors(); - this->loadTargetVariants(); - this->loadTargetRegisterDescriptors(); -} - -QJsonObject TargetDescriptionFile::getTargetDescriptionMapping() { - auto mappingFile = QFile( - QString::fromStdString(Paths::resourcesDirPath() + "/TargetDescriptionFiles/AVR/Mapping.json") - ); - - if (!mappingFile.exists()) { - throw Exception("Failed to load AVR target description mapping - mapping file not found"); - } - - mappingFile.open(QIODevice::ReadOnly); - return QJsonDocument::fromJson(mappingFile.readAll()).object(); -} - -TargetSignature TargetDescriptionFile::getTargetSignature() const { - auto& propertyGroups = this->propertyGroupsMappedByName; - auto signaturePropertyGroupIt = propertyGroups.find("signatures"); - - if (signaturePropertyGroupIt == propertyGroups.end()) { - throw TargetDescriptionParsingFailureException("Signature property group not found"); - } - - auto signaturePropertyGroup = signaturePropertyGroupIt->second; - auto& signatureProperties = signaturePropertyGroup.propertiesMappedByName; - std::optional signatureByteZero; - std::optional signatureByteOne; - std::optional signatureByteTwo; - - if (signatureProperties.contains("signature0")) { - signatureByteZero = static_cast( - signatureProperties.at("signature0").value.toShort(nullptr, 16) - ); - } - - if (signatureProperties.contains("signature1")) { - signatureByteOne = static_cast( - signatureProperties.at("signature1").value.toShort(nullptr, 16) - ); - } - - if (signatureProperties.contains("signature2")) { - signatureByteTwo = static_cast( - signatureProperties.at("signature2").value.toShort(nullptr, 16) - ); - } - - if (signatureByteZero.has_value() && signatureByteOne.has_value() && signatureByteTwo.has_value()) { - return TargetSignature(signatureByteZero.value(), signatureByteOne.value(), signatureByteTwo.value()); - } - - throw TargetDescriptionParsingFailureException("Failed to extract target signature from AVR8 target description."); -} - -Family TargetDescriptionFile::getFamily() const { - static auto familyNameToEnums = TargetDescriptionFile::getFamilyNameToEnumMapping(); - auto familyName = this->deviceElement.attributes().namedItem("family").nodeValue().toLower().toStdString(); - - if (familyName.empty()) { - throw Exception("Could not find target family name in target description file."); - } - - if (!familyNameToEnums.contains(familyName)) { - throw Exception("Unknown family name in target description file."); - } - - return familyNameToEnums.at(familyName); -} - -TargetParameters TargetDescriptionFile::getTargetParameters() const { - TargetParameters targetParameters; - - auto& peripheralModules = this->getPeripheralModulesMappedByName(); - auto& propertyGroups = this->getPropertyGroupsMappedByName(); - - auto flashMemorySegment = this->getFlashMemorySegment(); - if (flashMemorySegment.has_value()) { - targetParameters.flashSize = flashMemorySegment->size; - targetParameters.flashStartAddress = flashMemorySegment->startAddress; - - if (flashMemorySegment->pageSize.has_value()) { - targetParameters.flashPageSize = flashMemorySegment->pageSize.value(); + if (signatureProperties.contains("signature1")) { + signatureByteOne = static_cast( + signatureProperties.at("signature1").value.toShort(nullptr, 16) + ); } - } - auto ramMemorySegment = this->getRamMemorySegment(); - if (ramMemorySegment.has_value()) { - targetParameters.ramSize = ramMemorySegment->size; - targetParameters.ramStartAddress = ramMemorySegment->startAddress; - } - - auto ioMemorySegment = this->getIoMemorySegment(); - if (ioMemorySegment.has_value()) { - targetParameters.mappedIoSegmentSize = ioMemorySegment->size; - targetParameters.mappedIoSegmentStartAddress = ioMemorySegment->startAddress; - } - - auto registerMemorySegment = this->getRegisterMemorySegment(); - if (registerMemorySegment.has_value()) { - targetParameters.gpRegisterSize = registerMemorySegment->size; - targetParameters.gpRegisterStartAddress = registerMemorySegment->startAddress; - } - - auto eepromMemorySegment = this->getEepromMemorySegment(); - if (eepromMemorySegment.has_value()) { - targetParameters.eepromSize = eepromMemorySegment->size; - targetParameters.eepromStartAddress = eepromMemorySegment->startAddress; - - if (eepromMemorySegment->pageSize.has_value()) { - targetParameters.eepromPageSize = eepromMemorySegment->pageSize.value(); + if (signatureProperties.contains("signature2")) { + signatureByteTwo = static_cast( + signatureProperties.at("signature2").value.toShort(nullptr, 16) + ); } + + if (signatureByteZero.has_value() && signatureByteOne.has_value() && signatureByteTwo.has_value()) { + return TargetSignature(signatureByteZero.value(), signatureByteOne.value(), signatureByteTwo.value()); + } + + throw TargetDescriptionParsingFailureException( + "Failed to extract target signature from AVR8 target description." + ); } - auto firstBootSectionMemorySegment = this->getFirstBootSectionMemorySegment(); - if (firstBootSectionMemorySegment.has_value()) { - targetParameters.bootSectionStartAddress = firstBootSectionMemorySegment->startAddress / 2; - targetParameters.bootSectionSize = firstBootSectionMemorySegment->size; + Family TargetDescriptionFile::getFamily() const { + static auto familyNameToEnums = TargetDescriptionFile::getFamilyNameToEnumMapping(); + auto familyName = this->deviceElement.attributes().namedItem( + "family" + ).nodeValue().toLower().toStdString(); + + if (familyName.empty()) { + throw Exception("Could not find target family name in target description file."); + } + + if (!familyNameToEnums.contains(familyName)) { + throw Exception("Unknown family name in target description file."); + } + + return familyNameToEnums.at(familyName); } - std::uint32_t cpuRegistersOffset = 0; + TargetParameters TargetDescriptionFile::getTargetParameters() const { + TargetParameters targetParameters; - if (peripheralModules.contains("cpu")) { - auto cpuPeripheralModule = peripheralModules.at("cpu"); + auto& peripheralModules = this->getPeripheralModulesMappedByName(); + auto& propertyGroups = this->getPropertyGroupsMappedByName(); - if (cpuPeripheralModule.instancesMappedByName.contains("cpu")) { - auto cpuInstance = cpuPeripheralModule.instancesMappedByName.at("cpu"); + auto flashMemorySegment = this->getFlashMemorySegment(); + if (flashMemorySegment.has_value()) { + targetParameters.flashSize = flashMemorySegment->size; + targetParameters.flashStartAddress = flashMemorySegment->startAddress; - if (cpuInstance.registerGroupsMappedByName.contains("cpu")) { - cpuRegistersOffset = cpuInstance.registerGroupsMappedByName.at("cpu").offset.value_or(0); + if (flashMemorySegment->pageSize.has_value()) { + targetParameters.flashPageSize = flashMemorySegment->pageSize.value(); } } - } - auto statusRegister = this->getStatusRegister(); - if (statusRegister.has_value()) { - targetParameters.statusRegisterStartAddress = cpuRegistersOffset + statusRegister->offset; - targetParameters.statusRegisterSize = statusRegister->size; - } - - auto stackPointerRegister = this->getStackPointerRegister(); - if (stackPointerRegister.has_value()) { - targetParameters.stackPointerRegisterLowAddress = cpuRegistersOffset + stackPointerRegister->offset; - targetParameters.stackPointerRegisterSize = stackPointerRegister->size; - - } else { - // Sometimes the SP register is split into two register nodes, one for low, the other for high - auto stackPointerLowRegister = this->getStackPointerLowRegister(); - auto stackPointerHighRegister = this->getStackPointerHighRegister(); - - if (stackPointerLowRegister.has_value()) { - targetParameters.stackPointerRegisterLowAddress = cpuRegistersOffset - + stackPointerLowRegister->offset; - targetParameters.stackPointerRegisterSize = stackPointerLowRegister->size; + auto ramMemorySegment = this->getRamMemorySegment(); + if (ramMemorySegment.has_value()) { + targetParameters.ramSize = ramMemorySegment->size; + targetParameters.ramStartAddress = ramMemorySegment->startAddress; } - if (stackPointerHighRegister.has_value()) { - targetParameters.stackPointerRegisterSize = - targetParameters.stackPointerRegisterSize.has_value() ? - targetParameters.stackPointerRegisterSize.value() + stackPointerHighRegister->size : - stackPointerHighRegister->size; + auto ioMemorySegment = this->getIoMemorySegment(); + if (ioMemorySegment.has_value()) { + targetParameters.mappedIoSegmentSize = ioMemorySegment->size; + targetParameters.mappedIoSegmentStartAddress = ioMemorySegment->startAddress; } - } - auto supportedPhysicalInterfaces = this->getSupportedDebugPhysicalInterfaces(); - - if (supportedPhysicalInterfaces.contains(PhysicalInterface::DEBUG_WIRE) - || supportedPhysicalInterfaces.contains(PhysicalInterface::JTAG) - ) { - this->loadDebugWireAndJtagTargetParameters(targetParameters); - } - - if (supportedPhysicalInterfaces.contains(PhysicalInterface::PDI)) { - this->loadPdiTargetParameters(targetParameters); - } - - if (supportedPhysicalInterfaces.contains(PhysicalInterface::UPDI)) { - this->loadUpdiTargetParameters(targetParameters); - } - - return targetParameters; -} - -void TargetDescriptionFile::loadDebugPhysicalInterfaces() { - auto interfaceNamesToInterfaces = std::map({ - {"updi", PhysicalInterface::UPDI}, - {"debugwire", PhysicalInterface::DEBUG_WIRE}, - {"jtag", PhysicalInterface::DEBUG_WIRE}, - {"pdi", PhysicalInterface::PDI}, - }); - - for (const auto& [interfaceName, interface]: this->interfacesByName) { - if (interfaceNamesToInterfaces.contains(interfaceName)) { - this->supportedDebugPhysicalInterfaces.insert(interfaceNamesToInterfaces.at(interfaceName)); + auto registerMemorySegment = this->getRegisterMemorySegment(); + if (registerMemorySegment.has_value()) { + targetParameters.gpRegisterSize = registerMemorySegment->size; + targetParameters.gpRegisterStartAddress = registerMemorySegment->startAddress; } - } -} -void TargetDescriptionFile::loadPadDescriptors() { - auto& modules = this->getModulesMappedByName(); - auto portModule = (modules.contains("port")) ? std::optional(modules.find("port")->second) : std::nullopt; - auto& peripheralModules = this->getPeripheralModulesMappedByName(); + auto eepromMemorySegment = this->getEepromMemorySegment(); + if (eepromMemorySegment.has_value()) { + targetParameters.eepromSize = eepromMemorySegment->size; + targetParameters.eepromStartAddress = eepromMemorySegment->startAddress; - if (peripheralModules.contains("port")) { - auto portPeripheralModule = peripheralModules.find("port")->second; + if (eepromMemorySegment->pageSize.has_value()) { + targetParameters.eepromPageSize = eepromMemorySegment->pageSize.value(); + } + } - for (const auto& [instanceName, instance] : portPeripheralModule.instancesMappedByName) { - if (instanceName.find("port") == 0) { - auto portPeripheralRegisterGroup = (portPeripheralModule.registerGroupsMappedByName.contains(instanceName)) ? - std::optional(portPeripheralModule.registerGroupsMappedByName.find(instanceName)->second) : - std::nullopt; + auto firstBootSectionMemorySegment = this->getFirstBootSectionMemorySegment(); + if (firstBootSectionMemorySegment.has_value()) { + targetParameters.bootSectionStartAddress = firstBootSectionMemorySegment->startAddress / 2; + targetParameters.bootSectionSize = firstBootSectionMemorySegment->size; + } - for (const auto& signal : instance.instanceSignals) { - if (!signal.index.has_value()) { - continue; - } + std::uint32_t cpuRegistersOffset = 0; - auto padDescriptor = PadDescriptor(); - padDescriptor.name = signal.padName; - padDescriptor.gpioPinNumber = signal.index.value(); + if (peripheralModules.contains("cpu")) { + auto cpuPeripheralModule = peripheralModules.at("cpu"); - if (portModule.has_value() && portModule->registerGroupsMappedByName.contains(instanceName)) { - // We have register information for this port - auto registerGroup = portModule->registerGroupsMappedByName.find(instanceName)->second; + if (cpuPeripheralModule.instancesMappedByName.contains("cpu")) { + auto cpuInstance = cpuPeripheralModule.instancesMappedByName.at("cpu"); - for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) { - if (registerName.find("port") == 0) { - // This is the data register for the port - padDescriptor.gpioPortSetAddress = portRegister.offset; - padDescriptor.gpioPortClearAddress = portRegister.offset; - - } else if (registerName.find("pin") == 0) { - // This is the input data register for the port - padDescriptor.gpioPortInputAddress = portRegister.offset; - - } else if (registerName.find("ddr") == 0) { - // This is the data direction register for the port - padDescriptor.ddrSetAddress = portRegister.offset; - padDescriptor.ddrClearAddress = portRegister.offset; - } - } - - } else if (portModule.has_value() && portModule->registerGroupsMappedByName.contains("port")) { - // We have generic register information for all ports on the target - auto registerGroup = portModule->registerGroupsMappedByName.find("port")->second; - - for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) { - if (registerName.find("outset") == 0) { - // Include the port register offset - padDescriptor.gpioPortSetAddress = (portPeripheralRegisterGroup.has_value() - && portPeripheralRegisterGroup->offset.has_value()) ? - portPeripheralRegisterGroup->offset.value_or(0) : 0; - - padDescriptor.gpioPortSetAddress = padDescriptor.gpioPortSetAddress.value() - + portRegister.offset; - - } else if (registerName.find("outclr") == 0) { - padDescriptor.gpioPortClearAddress = (portPeripheralRegisterGroup.has_value() - && portPeripheralRegisterGroup->offset.has_value()) ? - portPeripheralRegisterGroup->offset.value_or(0) : 0; - - padDescriptor.gpioPortClearAddress = padDescriptor.gpioPortClearAddress.value() - + portRegister.offset; - - } else if (registerName.find("dirset") == 0) { - padDescriptor.ddrSetAddress = (portPeripheralRegisterGroup.has_value() - && portPeripheralRegisterGroup->offset.has_value()) ? - portPeripheralRegisterGroup->offset.value_or(0) : 0; - - padDescriptor.ddrSetAddress = padDescriptor.ddrSetAddress.value() - + portRegister.offset; - - } else if (registerName.find("dirclr") == 0) { - padDescriptor.ddrClearAddress = (portPeripheralRegisterGroup.has_value() - && portPeripheralRegisterGroup->offset.has_value()) ? - portPeripheralRegisterGroup->offset.value_or(0) : 0; - - padDescriptor.ddrClearAddress = padDescriptor.ddrClearAddress.value() - + portRegister.offset; - - } else if (registerName == "in") { - padDescriptor.gpioPortInputAddress = (portPeripheralRegisterGroup.has_value() - && portPeripheralRegisterGroup->offset.has_value()) ? - portPeripheralRegisterGroup->offset.value_or(0) : 0; - - padDescriptor.gpioPortInputAddress = padDescriptor.gpioPortInputAddress.value() - + portRegister.offset; - } - } - } - - this->padDescriptorsByName.insert(std::pair(padDescriptor.name, padDescriptor)); + if (cpuInstance.registerGroupsMappedByName.contains("cpu")) { + cpuRegistersOffset = cpuInstance.registerGroupsMappedByName.at("cpu").offset.value_or(0); } } } - } -} -void TargetDescriptionFile::loadTargetVariants() { - auto tdVariants = this->getVariants(); - auto tdPinoutsByName = this->getPinoutsMappedByName(); - auto& modules = this->getModulesMappedByName(); - - for (const auto& tdVariant : tdVariants) { - if (tdVariant.disabled) { - continue; + auto statusRegister = this->getStatusRegister(); + if (statusRegister.has_value()) { + targetParameters.statusRegisterStartAddress = cpuRegistersOffset + statusRegister->offset; + targetParameters.statusRegisterSize = statusRegister->size; } - auto targetVariant = TargetVariant(); - targetVariant.id = static_cast(this->targetVariantsById.size()); - targetVariant.name = tdVariant.name; - targetVariant.packageName = tdVariant.package; + auto stackPointerRegister = this->getStackPointerRegister(); + if (stackPointerRegister.has_value()) { + targetParameters.stackPointerRegisterLowAddress = cpuRegistersOffset + stackPointerRegister->offset; + targetParameters.stackPointerRegisterSize = stackPointerRegister->size; - if (tdVariant.package.find("QFP") == 0 || tdVariant.package.find("TQFP") == 0) { - targetVariant.package = TargetPackage::QFP; + } else { + // Sometimes the SP register is split into two register nodes, one for low, the other for high + auto stackPointerLowRegister = this->getStackPointerLowRegister(); + auto stackPointerHighRegister = this->getStackPointerHighRegister(); - } else if (tdVariant.package.find("PDIP") == 0 || tdVariant.package.find("DIP") == 0) { - targetVariant.package = TargetPackage::DIP; - - } else if (tdVariant.package.find("QFN") == 0 || tdVariant.package.find("VQFN") == 0) { - targetVariant.package = TargetPackage::QFN; - - } else if (tdVariant.package.find("SOIC") == 0) { - targetVariant.package = TargetPackage::SOIC; - - } else if (tdVariant.package.find("SSOP") == 0) { - targetVariant.package = TargetPackage::SSOP; - } - - if (!tdPinoutsByName.contains(tdVariant.pinoutName)) { - // Missing pinouts in the target description file - continue; - } - - auto tdPinout = tdPinoutsByName.find(tdVariant.pinoutName)->second; - for (const auto& tdPin : tdPinout.pins) { - auto targetPin = TargetPinDescriptor(); - targetPin.name = tdPin.pad; - targetPin.padName = tdPin.pad; - targetPin.number = tdPin.position; - targetPin.variantId = targetVariant.id; - - // TODO: REMOVE THIS: - if (tdPin.pad.find("vcc") == 0 - || tdPin.pad.find("avcc") == 0 - || tdPin.pad.find("aref") == 0 - || tdPin.pad.find("avdd") == 0 - || tdPin.pad.find("vdd") == 0 - ) { - targetPin.type = TargetPinType::VCC; - - } else if (tdPin.pad.find("gnd") == 0) { - targetPin.type = TargetPinType::GND; + if (stackPointerLowRegister.has_value()) { + targetParameters.stackPointerRegisterLowAddress = cpuRegistersOffset + + stackPointerLowRegister->offset; + targetParameters.stackPointerRegisterSize = stackPointerLowRegister->size; } - if (this->padDescriptorsByName.contains(targetPin.padName)) { - auto& pad = this->padDescriptorsByName.at(targetPin.padName); - if (pad.gpioPortSetAddress.has_value() && pad.ddrSetAddress.has_value()) { - targetPin.type = TargetPinType::GPIO; - } + if (stackPointerHighRegister.has_value()) { + targetParameters.stackPointerRegisterSize = + targetParameters.stackPointerRegisterSize.has_value() ? + targetParameters.stackPointerRegisterSize.value() + stackPointerHighRegister->size : + stackPointerHighRegister->size; } - - targetVariant.pinDescriptorsByNumber.insert(std::pair(targetPin.number, targetPin)); } - this->targetVariantsById.insert(std::pair(targetVariant.id, targetVariant)); + auto supportedPhysicalInterfaces = this->getSupportedDebugPhysicalInterfaces(); + + if (supportedPhysicalInterfaces.contains(PhysicalInterface::DEBUG_WIRE) + || supportedPhysicalInterfaces.contains(PhysicalInterface::JTAG) + ) { + this->loadDebugWireAndJtagTargetParameters(targetParameters); + } + + if (supportedPhysicalInterfaces.contains(PhysicalInterface::PDI)) { + this->loadPdiTargetParameters(targetParameters); + } + + if (supportedPhysicalInterfaces.contains(PhysicalInterface::UPDI)) { + this->loadUpdiTargetParameters(targetParameters); + } + + return targetParameters; } -} -void TargetDescriptionFile::loadTargetRegisterDescriptors() { - auto& modulesByName = this->modulesMappedByName; - auto& peripheralModulesByName = this->peripheralModulesMappedByName; + void TargetDescriptionFile::loadDebugPhysicalInterfaces() { + auto interfaceNamesToInterfaces = std::map({ + {"updi", PhysicalInterface::UPDI}, + {"debugwire", PhysicalInterface::DEBUG_WIRE}, + {"jtag", PhysicalInterface::DEBUG_WIRE}, + {"pdi", PhysicalInterface::PDI}, + }); - for (const auto& [moduleName, module] : modulesByName) { - for (const auto& [registerGroupName, registerGroup] : module.registerGroupsMappedByName) { - if (this->peripheralRegisterGroupsMappedByModuleRegisterGroupName.contains(registerGroupName)) { - auto& peripheralRegisterGroups = this->peripheralRegisterGroupsMappedByModuleRegisterGroupName - .at(registerGroupName); - for (const auto& peripheralRegisterGroup : peripheralRegisterGroups) { - if (peripheralRegisterGroup.addressSpaceId.value_or("") != "data") { - // Currently, we only deal with registers in the data address space. - continue; - } + for (const auto& [interfaceName, interface]: this->interfacesByName) { + if (interfaceNamesToInterfaces.contains(interfaceName)) { + this->supportedDebugPhysicalInterfaces.insert(interfaceNamesToInterfaces.at(interfaceName)); + } + } + } - for (const auto& [moduleRegisterName, moduleRegister] : registerGroup.registersMappedByName) { - if (moduleRegister.size < 1) { + void TargetDescriptionFile::loadPadDescriptors() { + const auto& modules = this->getModulesMappedByName(); + const auto portModule = (modules.contains("port")) ? std::optional(modules.find("port")->second) + : std::nullopt; + const auto& peripheralModules = this->getPeripheralModulesMappedByName(); + + if (peripheralModules.contains("port")) { + auto portPeripheralModule = peripheralModules.find("port")->second; + + for (const auto& [instanceName, instance] : portPeripheralModule.instancesMappedByName) { + if (instanceName.find("port") == 0) { + auto portPeripheralRegisterGroup = (portPeripheralModule.registerGroupsMappedByName.contains(instanceName)) ? + std::optional(portPeripheralModule.registerGroupsMappedByName.find(instanceName)->second) : + std::nullopt; + + for (const auto& signal : instance.instanceSignals) { + if (!signal.index.has_value()) { continue; } - auto registerDescriptor = TargetRegisterDescriptor(); - registerDescriptor.type = moduleName == "port" - ? TargetRegisterType::PORT_REGISTER : TargetRegisterType::OTHER; - registerDescriptor.memoryType = TargetMemoryType::RAM; - registerDescriptor.name = moduleRegisterName; - registerDescriptor.groupName = peripheralRegisterGroup.name; - registerDescriptor.size = moduleRegister.size; - registerDescriptor.startAddress = moduleRegister.offset - + peripheralRegisterGroup.offset.value_or(0); + auto padDescriptor = PadDescriptor(); + padDescriptor.name = signal.padName; + padDescriptor.gpioPinNumber = signal.index.value(); - if (moduleRegister.caption.has_value() && !moduleRegister.caption->empty()) { - registerDescriptor.description = moduleRegister.caption; + if (portModule.has_value() && portModule->registerGroupsMappedByName.contains(instanceName)) { + // We have register information for this port + auto registerGroup = portModule->registerGroupsMappedByName.find(instanceName)->second; + + for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) { + if (registerName.find("port") == 0) { + // This is the data register for the port + padDescriptor.gpioPortSetAddress = portRegister.offset; + padDescriptor.gpioPortClearAddress = portRegister.offset; + + } else if (registerName.find("pin") == 0) { + // This is the input data register for the port + padDescriptor.gpioPortInputAddress = portRegister.offset; + + } else if (registerName.find("ddr") == 0) { + // This is the data direction register for the port + padDescriptor.ddrSetAddress = portRegister.offset; + padDescriptor.ddrClearAddress = portRegister.offset; + } + } + + } else if (portModule.has_value() && portModule->registerGroupsMappedByName.contains("port")) { + // We have generic register information for all ports on the target + auto registerGroup = portModule->registerGroupsMappedByName.find("port")->second; + + for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) { + if (registerName.find("outset") == 0) { + // Include the port register offset + padDescriptor.gpioPortSetAddress = (portPeripheralRegisterGroup.has_value() + && portPeripheralRegisterGroup->offset.has_value()) ? + portPeripheralRegisterGroup->offset.value_or(0) : 0; + + padDescriptor.gpioPortSetAddress = padDescriptor.gpioPortSetAddress.value() + + portRegister.offset; + + } else if (registerName.find("outclr") == 0) { + padDescriptor.gpioPortClearAddress = (portPeripheralRegisterGroup.has_value() + && portPeripheralRegisterGroup->offset.has_value()) ? + portPeripheralRegisterGroup->offset.value_or(0) : 0; + + padDescriptor.gpioPortClearAddress = padDescriptor.gpioPortClearAddress.value() + + portRegister.offset; + + } else if (registerName.find("dirset") == 0) { + padDescriptor.ddrSetAddress = (portPeripheralRegisterGroup.has_value() + && portPeripheralRegisterGroup->offset.has_value()) ? + portPeripheralRegisterGroup->offset.value_or(0) : 0; + + padDescriptor.ddrSetAddress = padDescriptor.ddrSetAddress.value() + + portRegister.offset; + + } else if (registerName.find("dirclr") == 0) { + padDescriptor.ddrClearAddress = (portPeripheralRegisterGroup.has_value() + && portPeripheralRegisterGroup->offset.has_value()) ? + portPeripheralRegisterGroup->offset.value_or(0) : 0; + + padDescriptor.ddrClearAddress = padDescriptor.ddrClearAddress.value() + + portRegister.offset; + + } else if (registerName == "in") { + padDescriptor.gpioPortInputAddress = (portPeripheralRegisterGroup.has_value() + && portPeripheralRegisterGroup->offset.has_value()) ? + portPeripheralRegisterGroup->offset.value_or(0) : 0; + + padDescriptor.gpioPortInputAddress = padDescriptor.gpioPortInputAddress.value() + + portRegister.offset; + } + } } - if (moduleRegister.readWriteAccess.has_value()) { - auto& readWriteAccess = moduleRegister.readWriteAccess.value(); - registerDescriptor.readable = readWriteAccess.find('r') != std::string::npos; - registerDescriptor.writable = readWriteAccess.find('w') != std::string::npos; - - } else { - /* - * If the TDF doesn't specify the OCD read/write access for a register, we assume both - * are permitted. - */ - registerDescriptor.readable = true; - registerDescriptor.writable = true; - } - - this->targetRegisterDescriptorsByType[registerDescriptor.type].insert(registerDescriptor); + this->padDescriptorsByName.insert(std::pair(padDescriptor.name, padDescriptor)); } } } } } -} -std::optional TargetDescriptionFile::getFlashMemorySegment() const { - auto& addressMapping = this->addressSpacesMappedById; - auto programAddressSpaceIt = addressMapping.find("prog"); + void TargetDescriptionFile::loadTargetVariants() { + auto tdVariants = this->getVariants(); + auto tdPinoutsByName = this->getPinoutsMappedByName(); + const auto& modules = this->getModulesMappedByName(); - // Flash memory attributes are typically found in memory segments within the program address space. - if (programAddressSpaceIt != addressMapping.end()) { - auto& programAddressSpace = programAddressSpaceIt->second; - auto& programMemorySegments = programAddressSpace.memorySegmentsByTypeAndName; + for (const auto& tdVariant : tdVariants) { + if (tdVariant.disabled) { + continue; + } - if (programMemorySegments.find(MemorySegmentType::FLASH) != programMemorySegments.end()) { - auto& flashMemorySegments = programMemorySegments.find(MemorySegmentType::FLASH)->second; + auto targetVariant = TargetVariant(); + targetVariant.id = static_cast(this->targetVariantsById.size()); + targetVariant.name = tdVariant.name; + targetVariant.packageName = tdVariant.package; - /* - * In AVR8 TDFs, flash memory segments are typically named "APP_SECTION", "PROGMEM" or "FLASH". - */ - auto flashSegmentIt = flashMemorySegments.find("app_section") != flashMemorySegments.end() ? - flashMemorySegments.find("app_section") - : flashMemorySegments.find("progmem") != flashMemorySegments.end() - ? flashMemorySegments.find("progmem") : flashMemorySegments.find("flash"); + if (tdVariant.package.find("QFP") == 0 || tdVariant.package.find("TQFP") == 0) { + targetVariant.package = TargetPackage::QFP; - if (flashSegmentIt != flashMemorySegments.end()) { - return flashSegmentIt->second; + } else if (tdVariant.package.find("PDIP") == 0 || tdVariant.package.find("DIP") == 0) { + targetVariant.package = TargetPackage::DIP; + + } else if (tdVariant.package.find("QFN") == 0 || tdVariant.package.find("VQFN") == 0) { + targetVariant.package = TargetPackage::QFN; + + } else if (tdVariant.package.find("SOIC") == 0) { + targetVariant.package = TargetPackage::SOIC; + + } else if (tdVariant.package.find("SSOP") == 0) { + targetVariant.package = TargetPackage::SSOP; + } + + if (!tdPinoutsByName.contains(tdVariant.pinoutName)) { + // Missing pinouts in the target description file + continue; + } + + auto tdPinout = tdPinoutsByName.find(tdVariant.pinoutName)->second; + for (const auto& tdPin : tdPinout.pins) { + auto targetPin = TargetPinDescriptor(); + targetPin.name = tdPin.pad; + targetPin.padName = tdPin.pad; + targetPin.number = tdPin.position; + targetPin.variantId = targetVariant.id; + + // TODO: REMOVE THIS: + if (tdPin.pad.find("vcc") == 0 + || tdPin.pad.find("avcc") == 0 + || tdPin.pad.find("aref") == 0 + || tdPin.pad.find("avdd") == 0 + || tdPin.pad.find("vdd") == 0 + ) { + targetPin.type = TargetPinType::VCC; + + } else if (tdPin.pad.find("gnd") == 0) { + targetPin.type = TargetPinType::GND; + } + + if (this->padDescriptorsByName.contains(targetPin.padName)) { + auto& pad = this->padDescriptorsByName.at(targetPin.padName); + if (pad.gpioPortSetAddress.has_value() && pad.ddrSetAddress.has_value()) { + targetPin.type = TargetPinType::GPIO; + } + } + + targetVariant.pinDescriptorsByNumber.insert(std::pair(targetPin.number, targetPin)); + } + + this->targetVariantsById.insert(std::pair(targetVariant.id, targetVariant)); + } + } + + void TargetDescriptionFile::loadTargetRegisterDescriptors() { + auto& modulesByName = this->modulesMappedByName; + auto& peripheralModulesByName = this->peripheralModulesMappedByName; + + for (const auto& [moduleName, module] : modulesByName) { + for (const auto& [registerGroupName, registerGroup] : module.registerGroupsMappedByName) { + if (this->peripheralRegisterGroupsMappedByModuleRegisterGroupName.contains(registerGroupName)) { + auto& peripheralRegisterGroups = this->peripheralRegisterGroupsMappedByModuleRegisterGroupName + .at(registerGroupName); + for (const auto& peripheralRegisterGroup : peripheralRegisterGroups) { + if (peripheralRegisterGroup.addressSpaceId.value_or("") != "data") { + // Currently, we only deal with registers in the data address space. + continue; + } + + for (const auto& [moduleRegisterName, moduleRegister] : registerGroup.registersMappedByName) { + if (moduleRegister.size < 1) { + continue; + } + + auto registerDescriptor = TargetRegisterDescriptor(); + registerDescriptor.type = moduleName == "port" + ? TargetRegisterType::PORT_REGISTER : TargetRegisterType::OTHER; + registerDescriptor.memoryType = TargetMemoryType::RAM; + registerDescriptor.name = moduleRegisterName; + registerDescriptor.groupName = peripheralRegisterGroup.name; + registerDescriptor.size = moduleRegister.size; + registerDescriptor.startAddress = moduleRegister.offset + + peripheralRegisterGroup.offset.value_or(0); + + if (moduleRegister.caption.has_value() && !moduleRegister.caption->empty()) { + registerDescriptor.description = moduleRegister.caption; + } + + if (moduleRegister.readWriteAccess.has_value()) { + auto& readWriteAccess = moduleRegister.readWriteAccess.value(); + registerDescriptor.readable = readWriteAccess.find('r') != std::string::npos; + registerDescriptor.writable = readWriteAccess.find('w') != std::string::npos; + + } else { + /* + * If the TDF doesn't specify the OCD read/write access for a register, we assume both + * are permitted. + */ + registerDescriptor.readable = true; + registerDescriptor.writable = true; + } + + this->targetRegisterDescriptorsByType[registerDescriptor.type].insert(registerDescriptor); + } + } + } } } } - return std::nullopt; -} + std::optional TargetDescriptionFile::getFlashMemorySegment() const { + auto& addressMapping = this->addressSpacesMappedById; + auto programAddressSpaceIt = addressMapping.find("prog"); -std::optional TargetDescriptionFile::getRamMemorySegment() const { - auto& addressMapping = this->addressSpacesMappedById; + // Flash memory attributes are typically found in memory segments within the program address space. + if (programAddressSpaceIt != addressMapping.end()) { + auto& programAddressSpace = programAddressSpaceIt->second; + auto& programMemorySegments = programAddressSpace.memorySegmentsByTypeAndName; - // Internal RAM ®ister attributes are usually found in the data address space - auto dataAddressSpaceIt = addressMapping.find("data"); + if (programMemorySegments.find(MemorySegmentType::FLASH) != programMemorySegments.end()) { + auto& flashMemorySegments = programMemorySegments.find(MemorySegmentType::FLASH)->second; - if (dataAddressSpaceIt != addressMapping.end()) { - auto& dataAddressSpace = dataAddressSpaceIt->second; - auto& dataMemorySegments = dataAddressSpace.memorySegmentsByTypeAndName; + /* + * In AVR8 TDFs, flash memory segments are typically named "APP_SECTION", "PROGMEM" or "FLASH". + */ + auto flashSegmentIt = flashMemorySegments.find("app_section") != flashMemorySegments.end() ? + flashMemorySegments.find("app_section") + : flashMemorySegments.find("progmem") != flashMemorySegments.end() + ? flashMemorySegments.find("progmem") : flashMemorySegments.find("flash"); - if (dataMemorySegments.find(MemorySegmentType::RAM) != dataMemorySegments.end()) { - auto& ramMemorySegments = dataMemorySegments.find(MemorySegmentType::RAM)->second; - auto ramMemorySegmentIt = ramMemorySegments.begin(); - - if (ramMemorySegmentIt != ramMemorySegments.end()) { - return ramMemorySegmentIt->second; + if (flashSegmentIt != flashMemorySegments.end()) { + return flashSegmentIt->second; + } } } + + return std::nullopt; } - return std::nullopt; -} + std::optional TargetDescriptionFile::getRamMemorySegment() const { + auto& addressMapping = this->addressSpacesMappedById; -std::optional TargetDescriptionFile::getIoMemorySegment() const { - auto& addressMapping = this->addressSpacesMappedById; + // Internal RAM ®ister attributes are usually found in the data address space + auto dataAddressSpaceIt = addressMapping.find("data"); - if (addressMapping.contains("data")) { - auto& dataAddressSpace = addressMapping.at("data"); - auto& dataMemorySegments = dataAddressSpace.memorySegmentsByTypeAndName; + if (dataAddressSpaceIt != addressMapping.end()) { + auto& dataAddressSpace = dataAddressSpaceIt->second; + auto& dataMemorySegments = dataAddressSpace.memorySegmentsByTypeAndName; - if (dataMemorySegments.contains(MemorySegmentType::IO)) { - auto& ramMemorySegments = dataMemorySegments.at(MemorySegmentType::IO); - auto ramMemorySegmentIt = ramMemorySegments.begin(); + if (dataMemorySegments.find(MemorySegmentType::RAM) != dataMemorySegments.end()) { + auto& ramMemorySegments = dataMemorySegments.find(MemorySegmentType::RAM)->second; + auto ramMemorySegmentIt = ramMemorySegments.begin(); - if (ramMemorySegmentIt != ramMemorySegments.end()) { - return ramMemorySegmentIt->second; + if (ramMemorySegmentIt != ramMemorySegments.end()) { + return ramMemorySegmentIt->second; + } } } + + return std::nullopt; } - return std::nullopt; -} + std::optional TargetDescriptionFile::getIoMemorySegment() const { + auto& addressMapping = this->addressSpacesMappedById; -std::optional TargetDescriptionFile::getRegisterMemorySegment() const { - auto& addressMapping = this->addressSpacesMappedById; - - // Internal RAM ®ister attributes are usually found in the data address space - auto dataAddressSpaceIt = addressMapping.find("data"); - - if (dataAddressSpaceIt != addressMapping.end()) { - auto& dataAddressSpace = dataAddressSpaceIt->second; - auto& dataMemorySegments = dataAddressSpace.memorySegmentsByTypeAndName; - - if (dataMemorySegments.find(MemorySegmentType::REGISTERS) != dataMemorySegments.end()) { - auto& registerMemorySegments = dataMemorySegments.find(MemorySegmentType::REGISTERS)->second; - auto registerMemorySegmentIt = registerMemorySegments.begin(); - - if (registerMemorySegmentIt != registerMemorySegments.end()) { - return registerMemorySegmentIt->second; - } - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getEepromMemorySegment() const { - auto& addressMapping = this->addressSpacesMappedById; - - if (addressMapping.contains("eeprom")) { - auto& eepromAddressSpace = addressMapping.at("eeprom"); - auto& eepromAddressSpaceSegments = eepromAddressSpace.memorySegmentsByTypeAndName; - - if (eepromAddressSpaceSegments.contains(MemorySegmentType::EEPROM)) { - return eepromAddressSpaceSegments.at(MemorySegmentType::EEPROM).begin()->second; - } - - } else { - // The EEPROM memory segment may be part of the data address space if (addressMapping.contains("data")) { - auto dataAddressSpace = addressMapping.at("data"); + auto& dataAddressSpace = addressMapping.at("data"); + auto& dataMemorySegments = dataAddressSpace.memorySegmentsByTypeAndName; - if (dataAddressSpace.memorySegmentsByTypeAndName.contains(MemorySegmentType::EEPROM)) { - return dataAddressSpace.memorySegmentsByTypeAndName.at(MemorySegmentType::EEPROM).begin()->second; + if (dataMemorySegments.contains(MemorySegmentType::IO)) { + auto& ramMemorySegments = dataMemorySegments.at(MemorySegmentType::IO); + auto ramMemorySegmentIt = ramMemorySegments.begin(); + + if (ramMemorySegmentIt != ramMemorySegments.end()) { + return ramMemorySegmentIt->second; + } } } + + return std::nullopt; } - return std::nullopt; -} + std::optional TargetDescriptionFile::getRegisterMemorySegment() const { + auto& addressMapping = this->addressSpacesMappedById; -std::optional TargetDescriptionFile::getFirstBootSectionMemorySegment() const { - auto& addressMapping = this->addressSpacesMappedById; - auto programAddressSpaceIt = addressMapping.find("prog"); + // Internal RAM ®ister attributes are usually found in the data address space + auto dataAddressSpaceIt = addressMapping.find("data"); - if (programAddressSpaceIt != addressMapping.end()) { - auto& programAddressSpace = programAddressSpaceIt->second; - auto& programMemorySegments = programAddressSpace.memorySegmentsByTypeAndName; + if (dataAddressSpaceIt != addressMapping.end()) { + auto& dataAddressSpace = dataAddressSpaceIt->second; + auto& dataMemorySegments = dataAddressSpace.memorySegmentsByTypeAndName; - if (programMemorySegments.find(MemorySegmentType::FLASH) != programMemorySegments.end()) { - auto& flashMemorySegments = programMemorySegments.find(MemorySegmentType::FLASH)->second; + if (dataMemorySegments.find(MemorySegmentType::REGISTERS) != dataMemorySegments.end()) { + auto& registerMemorySegments = dataMemorySegments.find(MemorySegmentType::REGISTERS)->second; + auto registerMemorySegmentIt = registerMemorySegments.begin(); - if (flashMemorySegments.contains("boot_section_1")) { - return flashMemorySegments.at("boot_section_1"); - - } else if (flashMemorySegments.contains("boot_section")) { - return flashMemorySegments.at("boot_section"); + if (registerMemorySegmentIt != registerMemorySegments.end()) { + return registerMemorySegmentIt->second; + } } } + + return std::nullopt; } - return std::nullopt; -} + std::optional TargetDescriptionFile::getEepromMemorySegment() const { + auto& addressMapping = this->addressSpacesMappedById; -std::optional TargetDescriptionFile::getSignatureMemorySegment() const { - if (this->addressSpacesMappedById.contains("signatures")) { - auto& signaturesAddressSpace = this->addressSpacesMappedById.at("signatures"); - auto& signaturesAddressSpaceSegments = signaturesAddressSpace.memorySegmentsByTypeAndName; + if (addressMapping.contains("eeprom")) { + auto& eepromAddressSpace = addressMapping.at("eeprom"); + auto& eepromAddressSpaceSegments = eepromAddressSpace.memorySegmentsByTypeAndName; - if (signaturesAddressSpaceSegments.contains(MemorySegmentType::SIGNATURES)) { - return signaturesAddressSpaceSegments.at(MemorySegmentType::SIGNATURES).begin()->second; + if (eepromAddressSpaceSegments.contains(MemorySegmentType::EEPROM)) { + return eepromAddressSpaceSegments.at(MemorySegmentType::EEPROM).begin()->second; + } + + } else { + // The EEPROM memory segment may be part of the data address space + if (addressMapping.contains("data")) { + auto dataAddressSpace = addressMapping.at("data"); + + if (dataAddressSpace.memorySegmentsByTypeAndName.contains(MemorySegmentType::EEPROM)) { + return dataAddressSpace.memorySegmentsByTypeAndName.at(MemorySegmentType::EEPROM).begin()->second; + } + } } - } else { - // The signatures memory segment may be part of the data address space + return std::nullopt; + } + + std::optional TargetDescriptionFile::getFirstBootSectionMemorySegment() const { + auto& addressMapping = this->addressSpacesMappedById; + auto programAddressSpaceIt = addressMapping.find("prog"); + + if (programAddressSpaceIt != addressMapping.end()) { + auto& programAddressSpace = programAddressSpaceIt->second; + auto& programMemorySegments = programAddressSpace.memorySegmentsByTypeAndName; + + if (programMemorySegments.find(MemorySegmentType::FLASH) != programMemorySegments.end()) { + auto& flashMemorySegments = programMemorySegments.find(MemorySegmentType::FLASH)->second; + + if (flashMemorySegments.contains("boot_section_1")) { + return flashMemorySegments.at("boot_section_1"); + + } else if (flashMemorySegments.contains("boot_section")) { + return flashMemorySegments.at("boot_section"); + } + } + } + + return std::nullopt; + } + + std::optional TargetDescriptionFile::getSignatureMemorySegment() const { + if (this->addressSpacesMappedById.contains("signatures")) { + auto& signaturesAddressSpace = this->addressSpacesMappedById.at("signatures"); + auto& signaturesAddressSpaceSegments = signaturesAddressSpace.memorySegmentsByTypeAndName; + + if (signaturesAddressSpaceSegments.contains(MemorySegmentType::SIGNATURES)) { + return signaturesAddressSpaceSegments.at(MemorySegmentType::SIGNATURES).begin()->second; + } + + } else { + // The signatures memory segment may be part of the data address space + if (this->addressSpacesMappedById.contains("data")) { + auto dataAddressSpace = this->addressSpacesMappedById.at("data"); + + if (dataAddressSpace.memorySegmentsByTypeAndName.contains(MemorySegmentType::SIGNATURES)) { + auto& signatureSegmentsByName = dataAddressSpace.memorySegmentsByTypeAndName.at( + MemorySegmentType::SIGNATURES + ); + + if (signatureSegmentsByName.contains("signatures")) { + return signatureSegmentsByName.at("signatures"); + } + } + } + } + + return std::nullopt; + } + + std::optional TargetDescriptionFile::getFuseMemorySegment() const { if (this->addressSpacesMappedById.contains("data")) { auto dataAddressSpace = this->addressSpacesMappedById.at("data"); - if (dataAddressSpace.memorySegmentsByTypeAndName.contains(MemorySegmentType::SIGNATURES)) { - auto& signatureSegmentsByName = dataAddressSpace.memorySegmentsByTypeAndName.at( - MemorySegmentType::SIGNATURES - ); - - if (signatureSegmentsByName.contains("signatures")) { - return signatureSegmentsByName.at("signatures"); - } + if (dataAddressSpace.memorySegmentsByTypeAndName.contains(MemorySegmentType::FUSES)) { + return dataAddressSpace.memorySegmentsByTypeAndName.at( + MemorySegmentType::FUSES + ).begin()->second; } } + + return std::nullopt; } - return std::nullopt; -} + std::optional TargetDescriptionFile::getLockbitsMemorySegment() const { + if (this->addressSpacesMappedById.contains("data")) { + auto dataAddressSpace = this->addressSpacesMappedById.at("data"); -std::optional TargetDescriptionFile::getFuseMemorySegment() const { - if (this->addressSpacesMappedById.contains("data")) { - auto dataAddressSpace = this->addressSpacesMappedById.at("data"); - - if (dataAddressSpace.memorySegmentsByTypeAndName.contains(MemorySegmentType::FUSES)) { - return dataAddressSpace.memorySegmentsByTypeAndName.at( - MemorySegmentType::FUSES - ).begin()->second; + if (dataAddressSpace.memorySegmentsByTypeAndName.contains(MemorySegmentType::LOCKBITS)) { + return dataAddressSpace.memorySegmentsByTypeAndName.at( + MemorySegmentType::LOCKBITS + ).begin()->second; + } } + + return std::nullopt; } - return std::nullopt; -} + std::optional TargetDescriptionFile::getCpuRegisterGroup() const { + auto& modulesByName = this->modulesMappedByName; -std::optional TargetDescriptionFile::getLockbitsMemorySegment() const { - if (this->addressSpacesMappedById.contains("data")) { - auto dataAddressSpace = this->addressSpacesMappedById.at("data"); + if (modulesByName.find("cpu") != modulesByName.end()) { + auto cpuModule = modulesByName.find("cpu")->second; + auto cpuRegisterGroupIt = cpuModule.registerGroupsMappedByName.find("cpu"); - if (dataAddressSpace.memorySegmentsByTypeAndName.contains(MemorySegmentType::LOCKBITS)) { - return dataAddressSpace.memorySegmentsByTypeAndName.at( - MemorySegmentType::LOCKBITS - ).begin()->second; + if (cpuRegisterGroupIt != cpuModule.registerGroupsMappedByName.end()) { + return cpuRegisterGroupIt->second; + } } + + return std::nullopt; } - return std::nullopt; -} + std::optional TargetDescriptionFile::getBootLoadRegisterGroup() const { + auto& modulesByName = this->modulesMappedByName; -std::optional TargetDescriptionFile::getCpuRegisterGroup() const { - auto& modulesByName = this->modulesMappedByName; + if (modulesByName.contains("boot_load")) { + auto& bootLoadModule = modulesByName.at("boot_load"); + auto bootLoadRegisterGroupIt = bootLoadModule.registerGroupsMappedByName.find("boot_load"); - if (modulesByName.find("cpu") != modulesByName.end()) { - auto cpuModule = modulesByName.find("cpu")->second; - auto cpuRegisterGroupIt = cpuModule.registerGroupsMappedByName.find("cpu"); - - if (cpuRegisterGroupIt != cpuModule.registerGroupsMappedByName.end()) { - return cpuRegisterGroupIt->second; + if (bootLoadRegisterGroupIt != bootLoadModule.registerGroupsMappedByName.end()) { + return bootLoadRegisterGroupIt->second; + } } + + return std::nullopt; } - return std::nullopt; -} + std::optional TargetDescriptionFile::getEepromRegisterGroup() const { + auto& modulesByName = this->modulesMappedByName; -std::optional TargetDescriptionFile::getBootLoadRegisterGroup() const { - auto& modulesByName = this->modulesMappedByName; + if (modulesByName.find("eeprom") != modulesByName.end()) { + auto eepromModule = modulesByName.find("eeprom")->second; + auto eepromRegisterGroupIt = eepromModule.registerGroupsMappedByName.find("eeprom"); - if (modulesByName.contains("boot_load")) { - auto& bootLoadModule = modulesByName.at("boot_load"); - auto bootLoadRegisterGroupIt = bootLoadModule.registerGroupsMappedByName.find("boot_load"); - - if (bootLoadRegisterGroupIt != bootLoadModule.registerGroupsMappedByName.end()) { - return bootLoadRegisterGroupIt->second; + if (eepromRegisterGroupIt != eepromModule.registerGroupsMappedByName.end()) { + return eepromRegisterGroupIt->second; + } } + + return std::nullopt; } - return std::nullopt; -} - -std::optional TargetDescriptionFile::getEepromRegisterGroup() const { - auto& modulesByName = this->modulesMappedByName; - - if (modulesByName.find("eeprom") != modulesByName.end()) { - auto eepromModule = modulesByName.find("eeprom")->second; - auto eepromRegisterGroupIt = eepromModule.registerGroupsMappedByName.find("eeprom"); - - if (eepromRegisterGroupIt != eepromModule.registerGroupsMappedByName.end()) { - return eepromRegisterGroupIt->second; - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getStatusRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - - if (cpuRegisterGroup.has_value()) { - auto statusRegisterIt = cpuRegisterGroup->registersMappedByName.find("sreg"); - - if (statusRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { - return statusRegisterIt->second; - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getStackPointerRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - - if (cpuRegisterGroup.has_value()) { - auto stackPointerRegisterIt = cpuRegisterGroup->registersMappedByName.find("sp"); - - if (stackPointerRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { - return stackPointerRegisterIt->second; - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getStackPointerHighRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - - if (cpuRegisterGroup.has_value()) { - auto stackPointerHighRegisterIt = cpuRegisterGroup->registersMappedByName.find("sph"); - - if (stackPointerHighRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { - return stackPointerHighRegisterIt->second; - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getStackPointerLowRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - - if (cpuRegisterGroup.has_value()) { - auto stackPointerLowRegisterIt = cpuRegisterGroup->registersMappedByName.find("spl"); - - if (stackPointerLowRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { - return stackPointerLowRegisterIt->second; - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getOscillatorCalibrationRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - - if (cpuRegisterGroup.has_value()) { - auto& cpuRegisters = cpuRegisterGroup->registersMappedByName; - - if (cpuRegisters.contains("osccal")) { - return cpuRegisters.at("osccal"); - - } else if (cpuRegisters.contains("osccal0")) { - return cpuRegisters.at("osccal0"); - - } else if (cpuRegisters.contains("osccal1")) { - return cpuRegisters.at("osccal1"); - - } else if (cpuRegisters.contains("fosccal")) { - return cpuRegisters.at("fosccal"); - - } else if (cpuRegisters.contains("sosccala")) { - return cpuRegisters.at("sosccala"); - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getSpmcsRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - - if (cpuRegisterGroup.has_value() && cpuRegisterGroup->registersMappedByName.contains("spmcsr")) { - return cpuRegisterGroup->registersMappedByName.at("spmcsr"); - - } else { - auto bootLoadRegisterGroup = this->getBootLoadRegisterGroup(); - - if (bootLoadRegisterGroup.has_value() && bootLoadRegisterGroup->registersMappedByName.contains("spmcsr")) { - return bootLoadRegisterGroup->registersMappedByName.at("spmcsr"); - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getSpmcRegister() const { - auto bootLoadRegisterGroup = this->getBootLoadRegisterGroup(); - - if (bootLoadRegisterGroup.has_value() && bootLoadRegisterGroup->registersMappedByName.contains("spmcr")) { - return bootLoadRegisterGroup->registersMappedByName.at("spmcr"); - - } else { + std::optional TargetDescriptionFile::getStatusRegister() const { auto cpuRegisterGroup = this->getCpuRegisterGroup(); - if (cpuRegisterGroup.has_value() && cpuRegisterGroup->registersMappedByName.contains("spmcr")) { - return cpuRegisterGroup->registersMappedByName.at("spmcr"); - } - } + if (cpuRegisterGroup.has_value()) { + auto statusRegisterIt = cpuRegisterGroup->registersMappedByName.find("sreg"); - return std::nullopt; -} - -std::optional TargetDescriptionFile::getEepromAddressRegister() const { - auto eepromRegisterGroup = this->getEepromRegisterGroup(); - - if (eepromRegisterGroup.has_value()) { - auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eear"); - - if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { - return eepromAddressRegisterIt->second; - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getEepromAddressLowRegister() const { - auto eepromRegisterGroup = this->getEepromRegisterGroup(); - - if (eepromRegisterGroup.has_value()) { - auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eearl"); - - if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { - return eepromAddressRegisterIt->second; - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getEepromAddressHighRegister() const { - auto eepromRegisterGroup = this->getEepromRegisterGroup(); - - if (eepromRegisterGroup.has_value()) { - auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eearh"); - - if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { - return eepromAddressRegisterIt->second; - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getEepromDataRegister() const { - auto eepromRegisterGroup = this->getEepromRegisterGroup(); - - if (eepromRegisterGroup.has_value()) { - auto eepromDataRegisterIt = eepromRegisterGroup->registersMappedByName.find("eedr"); - - if (eepromDataRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { - return eepromDataRegisterIt->second; - } - } - - return std::nullopt; -} - -std::optional TargetDescriptionFile::getEepromControlRegister() const { - auto eepromRegisterGroup = this->getEepromRegisterGroup(); - - if (eepromRegisterGroup.has_value()) { - auto eepromControlRegisterIt = eepromRegisterGroup->registersMappedByName.find("eecr"); - - if (eepromControlRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { - return eepromControlRegisterIt->second; - } - } - - return std::nullopt; -} - -void TargetDescriptionFile::loadDebugWireAndJtagTargetParameters(TargetParameters& targetParameters) const { - auto& peripheralModules = this->getPeripheralModulesMappedByName(); - auto& propertyGroups = this->getPropertyGroupsMappedByName(); - - // OCD attributes can be found in property groups - if (propertyGroups.contains("ocd")) { - auto& ocdProperties = propertyGroups.at("ocd").propertiesMappedByName; - - if (ocdProperties.find("ocd_revision") != ocdProperties.end()) { - targetParameters.ocdRevision = ocdProperties.find("ocd_revision") - ->second.value.toUShort(nullptr, 10); - } - - if (ocdProperties.find("ocd_datareg") != ocdProperties.end()) { - targetParameters.ocdDataRegister = ocdProperties.find("ocd_datareg") - ->second.value.toUShort(nullptr, 16); - } - } - - auto spmcsRegister = this->getSpmcsRegister(); - if (spmcsRegister.has_value()) { - targetParameters.spmcRegisterStartAddress = spmcsRegister->offset; - - } else { - auto spmcRegister = this->getSpmcRegister(); - if (spmcRegister.has_value()) { - targetParameters.spmcRegisterStartAddress = spmcRegister->offset; - } - } - - auto osccalRegister = this->getOscillatorCalibrationRegister(); - if (osccalRegister.has_value()) { - targetParameters.osccalAddress = osccalRegister->offset; - } - - auto eepromAddressRegister = this->getEepromAddressRegister(); - if (eepromAddressRegister.has_value()) { - targetParameters.eepromAddressRegisterLow = eepromAddressRegister->offset; - targetParameters.eepromAddressRegisterHigh = (eepromAddressRegister->size == 2) - ? eepromAddressRegister->offset + 1 : eepromAddressRegister->offset; - - } else { - auto eepromAddressLowRegister = this->getEepromAddressLowRegister(); - if (eepromAddressLowRegister.has_value()) { - targetParameters.eepromAddressRegisterLow = eepromAddressLowRegister->offset; - auto eepromAddressHighRegister = this->getEepromAddressHighRegister(); - - if (eepromAddressHighRegister.has_value()) { - targetParameters.eepromAddressRegisterHigh = eepromAddressHighRegister->offset; - - } else { - targetParameters.eepromAddressRegisterHigh = eepromAddressLowRegister->offset; + if (statusRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { + return statusRegisterIt->second; } } + + return std::nullopt; } - auto eepromDataRegister = this->getEepromDataRegister(); - if (eepromDataRegister.has_value()) { - targetParameters.eepromDataRegisterAddress = eepromDataRegister->offset; + std::optional TargetDescriptionFile::getStackPointerRegister() const { + auto cpuRegisterGroup = this->getCpuRegisterGroup(); + + if (cpuRegisterGroup.has_value()) { + auto stackPointerRegisterIt = cpuRegisterGroup->registersMappedByName.find("sp"); + + if (stackPointerRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { + return stackPointerRegisterIt->second; + } + } + + return std::nullopt; } - auto eepromControlRegister = this->getEepromControlRegister(); - if (eepromControlRegister.has_value()) { - targetParameters.eepromControlRegisterAddress = eepromControlRegister->offset; + std::optional TargetDescriptionFile::getStackPointerHighRegister() const { + auto cpuRegisterGroup = this->getCpuRegisterGroup(); + + if (cpuRegisterGroup.has_value()) { + auto stackPointerHighRegisterIt = cpuRegisterGroup->registersMappedByName.find("sph"); + + if (stackPointerHighRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { + return stackPointerHighRegisterIt->second; + } + } + + return std::nullopt; } -} -void TargetDescriptionFile::loadPdiTargetParameters(TargetParameters& targetParameters) const { - auto& peripheralModules = this->getPeripheralModulesMappedByName(); - auto& propertyGroups = this->getPropertyGroupsMappedByName(); + std::optional TargetDescriptionFile::getStackPointerLowRegister() const { + auto cpuRegisterGroup = this->getCpuRegisterGroup(); - if (propertyGroups.contains("pdi_interface")) { - auto& pdiInterfaceProperties = propertyGroups.at("pdi_interface").propertiesMappedByName; + if (cpuRegisterGroup.has_value()) { + auto stackPointerLowRegisterIt = cpuRegisterGroup->registersMappedByName.find("spl"); - if (pdiInterfaceProperties.contains("app_section_offset")) { - targetParameters.appSectionPdiOffset = pdiInterfaceProperties - .at("app_section_offset").value.toUInt(nullptr, 16); + if (stackPointerLowRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { + return stackPointerLowRegisterIt->second; + } } - if (pdiInterfaceProperties.contains("boot_section_offset")) { - targetParameters.bootSectionPdiOffset = pdiInterfaceProperties - .at("boot_section_offset").value.toUInt(nullptr, 16); + return std::nullopt; + } + + std::optional TargetDescriptionFile::getOscillatorCalibrationRegister() const { + auto cpuRegisterGroup = this->getCpuRegisterGroup(); + + if (cpuRegisterGroup.has_value()) { + auto& cpuRegisters = cpuRegisterGroup->registersMappedByName; + + if (cpuRegisters.contains("osccal")) { + return cpuRegisters.at("osccal"); + + } else if (cpuRegisters.contains("osccal0")) { + return cpuRegisters.at("osccal0"); + + } else if (cpuRegisters.contains("osccal1")) { + return cpuRegisters.at("osccal1"); + + } else if (cpuRegisters.contains("fosccal")) { + return cpuRegisters.at("fosccal"); + + } else if (cpuRegisters.contains("sosccala")) { + return cpuRegisters.at("sosccala"); + } } - if (pdiInterfaceProperties.contains("datamem_offset")) { - targetParameters.ramPdiOffset = pdiInterfaceProperties - .at("datamem_offset").value.toUInt(nullptr, 16); + return std::nullopt; + } + + std::optional TargetDescriptionFile::getSpmcsRegister() const { + auto cpuRegisterGroup = this->getCpuRegisterGroup(); + + if (cpuRegisterGroup.has_value() && cpuRegisterGroup->registersMappedByName.contains("spmcsr")) { + return cpuRegisterGroup->registersMappedByName.at("spmcsr"); + + } else { + auto bootLoadRegisterGroup = this->getBootLoadRegisterGroup(); + + if (bootLoadRegisterGroup.has_value() + && bootLoadRegisterGroup->registersMappedByName.contains("spmcsr") + ) { + return bootLoadRegisterGroup->registersMappedByName.at("spmcsr"); + } } - if (pdiInterfaceProperties.contains("eeprom_offset")) { - targetParameters.eepromPdiOffset = pdiInterfaceProperties - .at("eeprom_offset").value.toUInt(nullptr, 16); + return std::nullopt; + } + + std::optional TargetDescriptionFile::getSpmcRegister() const { + auto bootLoadRegisterGroup = this->getBootLoadRegisterGroup(); + + if (bootLoadRegisterGroup.has_value() && bootLoadRegisterGroup->registersMappedByName.contains("spmcr")) { + return bootLoadRegisterGroup->registersMappedByName.at("spmcr"); + + } else { + auto cpuRegisterGroup = this->getCpuRegisterGroup(); + + if (cpuRegisterGroup.has_value() && cpuRegisterGroup->registersMappedByName.contains("spmcr")) { + return cpuRegisterGroup->registersMappedByName.at("spmcr"); + } } - if (pdiInterfaceProperties.contains("user_signatures_offset")) { - targetParameters.userSignaturesPdiOffset = pdiInterfaceProperties - .at("user_signatures_offset").value.toUInt(nullptr, 16); + return std::nullopt; + } + + std::optional TargetDescriptionFile::getEepromAddressRegister() const { + auto eepromRegisterGroup = this->getEepromRegisterGroup(); + + if (eepromRegisterGroup.has_value()) { + auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eear"); + + if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { + return eepromAddressRegisterIt->second; + } } - if (pdiInterfaceProperties.contains("prod_signatures_offset")) { - targetParameters.productSignaturesPdiOffset = pdiInterfaceProperties - .at("prod_signatures_offset").value.toUInt(nullptr, 16); + return std::nullopt; + } + + std::optional TargetDescriptionFile::getEepromAddressLowRegister() const { + auto eepromRegisterGroup = this->getEepromRegisterGroup(); + + if (eepromRegisterGroup.has_value()) { + auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eearl"); + + if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { + return eepromAddressRegisterIt->second; + } } - if (pdiInterfaceProperties.contains("fuse_registers_offset")) { - targetParameters.fuseRegistersPdiOffset = pdiInterfaceProperties - .at("fuse_registers_offset").value.toUInt(nullptr, 16); + return std::nullopt; + } + + std::optional TargetDescriptionFile::getEepromAddressHighRegister() const { + auto eepromRegisterGroup = this->getEepromRegisterGroup(); + + if (eepromRegisterGroup.has_value()) { + auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eearh"); + + if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { + return eepromAddressRegisterIt->second; + } } - if (pdiInterfaceProperties.contains("lock_registers_offset")) { - targetParameters.lockRegistersPdiOffset = pdiInterfaceProperties - .at("lock_registers_offset").value.toUInt(nullptr, 16); + return std::nullopt; + } + + std::optional TargetDescriptionFile::getEepromDataRegister() const { + auto eepromRegisterGroup = this->getEepromRegisterGroup(); + + if (eepromRegisterGroup.has_value()) { + auto eepromDataRegisterIt = eepromRegisterGroup->registersMappedByName.find("eedr"); + + if (eepromDataRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { + return eepromDataRegisterIt->second; + } } - if (peripheralModules.contains("nvm")) { - auto& nvmModule = peripheralModules.at("nvm"); + return std::nullopt; + } - if (nvmModule.instancesMappedByName.contains("nvm")) { - auto& nvmInstance = nvmModule.instancesMappedByName.at("nvm"); + std::optional TargetDescriptionFile::getEepromControlRegister() const { + auto eepromRegisterGroup = this->getEepromRegisterGroup(); - if (nvmInstance.registerGroupsMappedByName.contains("nvm")) { - targetParameters.nvmBaseAddress = nvmInstance.registerGroupsMappedByName.at("nvm").offset; + if (eepromRegisterGroup.has_value()) { + auto eepromControlRegisterIt = eepromRegisterGroup->registersMappedByName.find("eecr"); + + if (eepromControlRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { + return eepromControlRegisterIt->second; + } + } + + return std::nullopt; + } + + void TargetDescriptionFile::loadDebugWireAndJtagTargetParameters(TargetParameters& targetParameters) const { + auto& peripheralModules = this->getPeripheralModulesMappedByName(); + auto& propertyGroups = this->getPropertyGroupsMappedByName(); + + // OCD attributes can be found in property groups + if (propertyGroups.contains("ocd")) { + auto& ocdProperties = propertyGroups.at("ocd").propertiesMappedByName; + + if (ocdProperties.find("ocd_revision") != ocdProperties.end()) { + targetParameters.ocdRevision = ocdProperties.find("ocd_revision") + ->second.value.toUShort(nullptr, 10); + } + + if (ocdProperties.find("ocd_datareg") != ocdProperties.end()) { + targetParameters.ocdDataRegister = ocdProperties.find("ocd_datareg") + ->second.value.toUShort(nullptr, 16); + } + } + + auto spmcsRegister = this->getSpmcsRegister(); + if (spmcsRegister.has_value()) { + targetParameters.spmcRegisterStartAddress = spmcsRegister->offset; + + } else { + auto spmcRegister = this->getSpmcRegister(); + if (spmcRegister.has_value()) { + targetParameters.spmcRegisterStartAddress = spmcRegister->offset; + } + } + + auto osccalRegister = this->getOscillatorCalibrationRegister(); + if (osccalRegister.has_value()) { + targetParameters.osccalAddress = osccalRegister->offset; + } + + auto eepromAddressRegister = this->getEepromAddressRegister(); + if (eepromAddressRegister.has_value()) { + targetParameters.eepromAddressRegisterLow = eepromAddressRegister->offset; + targetParameters.eepromAddressRegisterHigh = (eepromAddressRegister->size == 2) + ? eepromAddressRegister->offset + 1 : eepromAddressRegister->offset; + + } else { + auto eepromAddressLowRegister = this->getEepromAddressLowRegister(); + if (eepromAddressLowRegister.has_value()) { + targetParameters.eepromAddressRegisterLow = eepromAddressLowRegister->offset; + auto eepromAddressHighRegister = this->getEepromAddressHighRegister(); + + if (eepromAddressHighRegister.has_value()) { + targetParameters.eepromAddressRegisterHigh = eepromAddressHighRegister->offset; + + } else { + targetParameters.eepromAddressRegisterHigh = eepromAddressLowRegister->offset; + } + } + } + + auto eepromDataRegister = this->getEepromDataRegister(); + if (eepromDataRegister.has_value()) { + targetParameters.eepromDataRegisterAddress = eepromDataRegister->offset; + } + + auto eepromControlRegister = this->getEepromControlRegister(); + if (eepromControlRegister.has_value()) { + targetParameters.eepromControlRegisterAddress = eepromControlRegister->offset; + } + } + + void TargetDescriptionFile::loadPdiTargetParameters(TargetParameters& targetParameters) const { + auto& peripheralModules = this->getPeripheralModulesMappedByName(); + auto& propertyGroups = this->getPropertyGroupsMappedByName(); + + if (propertyGroups.contains("pdi_interface")) { + auto& pdiInterfaceProperties = propertyGroups.at("pdi_interface").propertiesMappedByName; + + if (pdiInterfaceProperties.contains("app_section_offset")) { + targetParameters.appSectionPdiOffset = pdiInterfaceProperties + .at("app_section_offset").value.toUInt(nullptr, 16); + } + + if (pdiInterfaceProperties.contains("boot_section_offset")) { + targetParameters.bootSectionPdiOffset = pdiInterfaceProperties + .at("boot_section_offset").value.toUInt(nullptr, 16); + } + + if (pdiInterfaceProperties.contains("datamem_offset")) { + targetParameters.ramPdiOffset = pdiInterfaceProperties + .at("datamem_offset").value.toUInt(nullptr, 16); + } + + if (pdiInterfaceProperties.contains("eeprom_offset")) { + targetParameters.eepromPdiOffset = pdiInterfaceProperties + .at("eeprom_offset").value.toUInt(nullptr, 16); + } + + if (pdiInterfaceProperties.contains("user_signatures_offset")) { + targetParameters.userSignaturesPdiOffset = pdiInterfaceProperties + .at("user_signatures_offset").value.toUInt(nullptr, 16); + } + + if (pdiInterfaceProperties.contains("prod_signatures_offset")) { + targetParameters.productSignaturesPdiOffset = pdiInterfaceProperties + .at("prod_signatures_offset").value.toUInt(nullptr, 16); + } + + if (pdiInterfaceProperties.contains("fuse_registers_offset")) { + targetParameters.fuseRegistersPdiOffset = pdiInterfaceProperties + .at("fuse_registers_offset").value.toUInt(nullptr, 16); + } + + if (pdiInterfaceProperties.contains("lock_registers_offset")) { + targetParameters.lockRegistersPdiOffset = pdiInterfaceProperties + .at("lock_registers_offset").value.toUInt(nullptr, 16); + } + + if (peripheralModules.contains("nvm")) { + auto& nvmModule = peripheralModules.at("nvm"); + + if (nvmModule.instancesMappedByName.contains("nvm")) { + auto& nvmInstance = nvmModule.instancesMappedByName.at("nvm"); + + if (nvmInstance.registerGroupsMappedByName.contains("nvm")) { + targetParameters.nvmBaseAddress = nvmInstance.registerGroupsMappedByName.at("nvm").offset; + } } } } } -} -void TargetDescriptionFile::loadUpdiTargetParameters(TargetParameters& targetParameters) const { - auto& propertyGroups = this->getPropertyGroupsMappedByName(); - auto& peripheralModules = this->getPeripheralModulesMappedByName(); - auto modulesByName = this->getModulesMappedByName(); + void TargetDescriptionFile::loadUpdiTargetParameters(TargetParameters& targetParameters) const { + auto& propertyGroups = this->getPropertyGroupsMappedByName(); + auto& peripheralModules = this->getPeripheralModulesMappedByName(); + auto modulesByName = this->getModulesMappedByName(); - if (peripheralModules.contains("nvmctrl")) { - auto& nvmCtrlModule = peripheralModules.at("nvmctrl"); + if (peripheralModules.contains("nvmctrl")) { + auto& nvmCtrlModule = peripheralModules.at("nvmctrl"); - if (nvmCtrlModule.instancesMappedByName.contains("nvmctrl")) { - auto& nvmCtrlInstance = nvmCtrlModule.instancesMappedByName.at("nvmctrl"); + if (nvmCtrlModule.instancesMappedByName.contains("nvmctrl")) { + auto& nvmCtrlInstance = nvmCtrlModule.instancesMappedByName.at("nvmctrl"); - if (nvmCtrlInstance.registerGroupsMappedByName.contains("nvmctrl")) { - targetParameters.nvmBaseAddress = nvmCtrlInstance.registerGroupsMappedByName.at("nvmctrl").offset; + if (nvmCtrlInstance.registerGroupsMappedByName.contains("nvmctrl")) { + targetParameters.nvmBaseAddress = nvmCtrlInstance.registerGroupsMappedByName.at( + "nvmctrl" + ).offset; + } } } - } - if (propertyGroups.contains("updi_interface")) { - auto& updiInterfaceProperties = propertyGroups.at("updi_interface").propertiesMappedByName; + if (propertyGroups.contains("updi_interface")) { + auto& updiInterfaceProperties = propertyGroups.at("updi_interface").propertiesMappedByName; - if (updiInterfaceProperties.contains("ocd_base_addr")) { - targetParameters.ocdModuleAddress = updiInterfaceProperties - .at("ocd_base_addr").value.toUShort(nullptr, 16); + if (updiInterfaceProperties.contains("ocd_base_addr")) { + targetParameters.ocdModuleAddress = updiInterfaceProperties + .at("ocd_base_addr").value.toUShort(nullptr, 16); + } + + if (updiInterfaceProperties.contains("progmem_offset")) { + targetParameters.programMemoryUpdiStartAddress = updiInterfaceProperties + .at("progmem_offset").value.toUInt(nullptr, 16); + } } - if (updiInterfaceProperties.contains("progmem_offset")) { - targetParameters.programMemoryUpdiStartAddress = updiInterfaceProperties - .at("progmem_offset").value.toUInt(nullptr, 16); + auto signatureMemorySegment = this->getSignatureMemorySegment(); + if (signatureMemorySegment.has_value()) { + targetParameters.signatureSegmentStartAddress = signatureMemorySegment->startAddress; + targetParameters.signatureSegmentSize = signatureMemorySegment->size; } - } - auto signatureMemorySegment = this->getSignatureMemorySegment(); - if (signatureMemorySegment.has_value()) { - targetParameters.signatureSegmentStartAddress = signatureMemorySegment->startAddress; - targetParameters.signatureSegmentSize = signatureMemorySegment->size; - } + auto fuseMemorySegment = this->getFuseMemorySegment(); + if (fuseMemorySegment.has_value()) { + targetParameters.fuseSegmentStartAddress = fuseMemorySegment->startAddress; + targetParameters.fuseSegmentSize = fuseMemorySegment->size; + } - auto fuseMemorySegment = this->getFuseMemorySegment(); - if (fuseMemorySegment.has_value()) { - targetParameters.fuseSegmentStartAddress = fuseMemorySegment->startAddress; - targetParameters.fuseSegmentSize = fuseMemorySegment->size; - } - - auto lockbitsMemorySegment = this->getLockbitsMemorySegment(); - if (lockbitsMemorySegment.has_value()) { - targetParameters.lockbitsSegmentStartAddress = lockbitsMemorySegment->startAddress; + auto lockbitsMemorySegment = this->getLockbitsMemorySegment(); + if (lockbitsMemorySegment.has_value()) { + targetParameters.lockbitsSegmentStartAddress = lockbitsMemorySegment->startAddress; + } } } diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/TargetDescription/TargetDescriptionFile.cpp index a3d2c4a4..34477dd6 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.cpp @@ -7,489 +7,510 @@ #include "src/Logger/Logger.hpp" #include "src/Helpers/Paths.hpp" -using namespace Bloom::Targets::TargetDescription; -using namespace Bloom::Exceptions; +namespace Bloom::Targets::TargetDescription +{ + using namespace Bloom::Exceptions; -std::string TargetDescriptionFile::getTargetName() const { - return this->deviceElement.attributes().namedItem("name").nodeValue().toStdString(); -} - -void TargetDescriptionFile::init(const QString& xmlFilePath) { - auto file = QFile(xmlFilePath); - if (!file.exists()) { - // This can happen if someone has been messing with the Resources directory. - throw Exception("Failed to load target description file - file not found"); + std::string TargetDescriptionFile::getTargetName() const { + return this->deviceElement.attributes().namedItem("name").nodeValue().toStdString(); } - file.open(QIODevice::ReadOnly); - auto xml = QDomDocument(); - if (!xml.setContent(file.readAll())) { - throw Exception("Failed to parse target description file - please report this error " - "to Bloom developers via " + Paths::homeDomainName() + "/report-issue"); - } - - this->init(xml); -} - -void TargetDescriptionFile::init(const QDomDocument& xml) { - this->xml = xml; - - auto device = xml.elementsByTagName("device").item(0).toElement(); - if (!device.isElement()) { - throw TargetDescriptionParsingFailureException("Device element not found."); - } - - this->deviceElement = device; - - this->loadAddressSpaces(); - this->loadPropertyGroups(); - this->loadModules(); - this->loadPeripheralModules(); - this->loadVariants(); - this->loadPinouts(); - this->loadInterfaces(); -} - -AddressSpace TargetDescriptionFile::generateAddressSpaceFromXml(const QDomElement& xmlElement) { - if ( - !xmlElement.hasAttribute("id") - || !xmlElement.hasAttribute("name") - || !xmlElement.hasAttribute("size") - || !xmlElement.hasAttribute("start") - ) { - throw Exception("Address space element missing id/name/size/start attributes."); - } - - auto addressSpace = AddressSpace(); - addressSpace.name = xmlElement.attribute("name").toStdString(); - addressSpace.id = xmlElement.attribute("id").toStdString(); - - bool conversionStatus; - addressSpace.startAddress = xmlElement.attribute("start").toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to convert start address hex value to integer."); - } - - addressSpace.size = xmlElement.attribute("size").toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to convert size hex value to integer."); - } - - if (xmlElement.hasAttribute("endianness")) { - addressSpace.littleEndian = (xmlElement.attribute("endianness").toStdString() == "little"); - } - - // Create memory segment objects and add them to the mapping. - auto segmentNodes = xmlElement.elementsByTagName("memory-segment"); - auto& memorySegments = addressSpace.memorySegmentsByTypeAndName; - for (int segmentIndex = 0; segmentIndex < segmentNodes.count(); segmentIndex++) { - try { - auto segment = TargetDescriptionFile::generateMemorySegmentFromXml( - segmentNodes.item(segmentIndex).toElement() - ); - - if (!memorySegments.contains(segment.type)) { - memorySegments.insert( - std::pair< - MemorySegmentType, - decltype(addressSpace.memorySegmentsByTypeAndName)::mapped_type - >(segment.type, {})); - } - - memorySegments.find(segment.type)->second.insert(std::pair(segment.name, segment)); - - } catch (const Exception& exception) { - Logger::debug("Failed to extract memory segment from target description element - " - + exception.getMessage()); + void TargetDescriptionFile::init(const QString& xmlFilePath) { + auto file = QFile(xmlFilePath); + if (!file.exists()) { + // This can happen if someone has been messing with the Resources directory. + throw Exception("Failed to load target description file - file not found"); } + + file.open(QIODevice::ReadOnly); + auto xml = QDomDocument(); + if (!xml.setContent(file.readAll())) { + throw Exception("Failed to parse target description file - please report this error " + "to Bloom developers via " + Paths::homeDomainName() + "/report-issue"); + } + + this->init(xml); } - return addressSpace; -} + void TargetDescriptionFile::init(const QDomDocument& xml) { + this->xml = xml; -MemorySegment TargetDescriptionFile::generateMemorySegmentFromXml(const QDomElement& xmlElement) { - if ( - !xmlElement.hasAttribute("type") - || !xmlElement.hasAttribute("name") - || !xmlElement.hasAttribute("size") - || !xmlElement.hasAttribute("start") - ) { - throw Exception("Missing type/name/size/start attributes"); + auto device = xml.elementsByTagName("device").item(0).toElement(); + if (!device.isElement()) { + throw TargetDescriptionParsingFailureException("Device element not found."); + } + + this->deviceElement = device; + + this->loadAddressSpaces(); + this->loadPropertyGroups(); + this->loadModules(); + this->loadPeripheralModules(); + this->loadVariants(); + this->loadPinouts(); + this->loadInterfaces(); } - auto segment = MemorySegment(); - auto typeName = xmlElement.attribute("type").toStdString(); - auto type = MemorySegment::typesMappedByName.valueAt(typeName); + AddressSpace TargetDescriptionFile::generateAddressSpaceFromXml(const QDomElement& xmlElement) { + if ( + !xmlElement.hasAttribute("id") + || !xmlElement.hasAttribute("name") + || !xmlElement.hasAttribute("size") + || !xmlElement.hasAttribute("start") + ) { + throw Exception("Address space element missing id/name/size/start attributes."); + } - if (!type.has_value()) { - throw Exception("Unknown type: \"" + typeName + "\""); + auto addressSpace = AddressSpace(); + addressSpace.name = xmlElement.attribute("name").toStdString(); + addressSpace.id = xmlElement.attribute("id").toStdString(); + + bool conversionStatus; + addressSpace.startAddress = xmlElement.attribute("start").toUInt(&conversionStatus, 16); + + if (!conversionStatus) { + throw Exception("Failed to convert start address hex value to integer."); + } + + addressSpace.size = xmlElement.attribute("size").toUInt(&conversionStatus, 16); + + if (!conversionStatus) { + throw Exception("Failed to convert size hex value to integer."); + } + + if (xmlElement.hasAttribute("endianness")) { + addressSpace.littleEndian = (xmlElement.attribute("endianness").toStdString() == "little"); + } + + // Create memory segment objects and add them to the mapping. + auto segmentNodes = xmlElement.elementsByTagName("memory-segment"); + auto& memorySegments = addressSpace.memorySegmentsByTypeAndName; + for (int segmentIndex = 0; segmentIndex < segmentNodes.count(); segmentIndex++) { + try { + auto segment = TargetDescriptionFile::generateMemorySegmentFromXml( + segmentNodes.item(segmentIndex).toElement() + ); + + if (!memorySegments.contains(segment.type)) { + memorySegments.insert( + std::pair< + MemorySegmentType, + decltype(addressSpace.memorySegmentsByTypeAndName)::mapped_type + >(segment.type, {})); + } + + memorySegments.find(segment.type)->second.insert(std::pair(segment.name, segment)); + + } catch (const Exception& exception) { + Logger::debug("Failed to extract memory segment from target description element - " + + exception.getMessage()); + } + } + + return addressSpace; } - segment.type = type.value(); - segment.name = xmlElement.attribute("name").toLower().toStdString(); + MemorySegment TargetDescriptionFile::generateMemorySegmentFromXml(const QDomElement& xmlElement) { + if ( + !xmlElement.hasAttribute("type") + || !xmlElement.hasAttribute("name") + || !xmlElement.hasAttribute("size") + || !xmlElement.hasAttribute("start") + ) { + throw Exception("Missing type/name/size/start attributes"); + } - bool conversionStatus; - segment.startAddress = xmlElement.attribute("start").toUInt(&conversionStatus, 16); + auto segment = MemorySegment(); + auto typeName = xmlElement.attribute("type").toStdString(); + auto type = MemorySegment::typesMappedByName.valueAt(typeName); - if (!conversionStatus) { - // Failed to convert startAddress hex value as string to uint16_t - throw Exception("Invalid start address"); - } + if (!type.has_value()) { + throw Exception("Unknown type: \"" + typeName + "\""); + } - segment.size = xmlElement.attribute("size").toUInt(&conversionStatus, 16); + segment.type = type.value(); + segment.name = xmlElement.attribute("name").toLower().toStdString(); - if (!conversionStatus) { - // Failed to convert size hex value as string to uint16_t - throw Exception("Invalid size"); - } + bool conversionStatus = false; + segment.startAddress = xmlElement.attribute("start").toUInt(&conversionStatus, 16); - if (xmlElement.hasAttribute("pagesize")) { - // The page size can be in single byte hexadecimal form ("0x01"), or it can be in plain integer form! - auto pageSize = xmlElement.attribute("pagesize"); - segment.pageSize = pageSize.toUInt(&conversionStatus, pageSize.contains("0x") ? 16 : 10); + if (!conversionStatus) { + // Failed to convert startAddress hex value as string to uint16_t + throw Exception("Invalid start address"); + } + + segment.size = xmlElement.attribute("size").toUInt(&conversionStatus, 16); if (!conversionStatus) { // Failed to convert size hex value as string to uint16_t throw Exception("Invalid size"); } - } - return segment; -} + if (xmlElement.hasAttribute("pagesize")) { + // The page size can be in single byte hexadecimal form ("0x01"), or it can be in plain integer form! + auto pageSize = xmlElement.attribute("pagesize"); + segment.pageSize = pageSize.toUInt(&conversionStatus, pageSize.contains("0x") ? 16 : 10); -RegisterGroup TargetDescriptionFile::generateRegisterGroupFromXml(const QDomElement& xmlElement) { - if (!xmlElement.hasAttribute("name")) { - throw Exception("Missing register group name attribute"); - } - - auto registerGroup = RegisterGroup(); - registerGroup.name = xmlElement.attribute("name").toLower().toStdString(); - - if (registerGroup.name.empty()) { - throw Exception("Empty register group name"); - } - - if (xmlElement.hasAttribute("name-in-module")) { - registerGroup.moduleName = xmlElement.attribute("name-in-module").toLower().toStdString(); - } - - if (xmlElement.hasAttribute("address-space")) { - registerGroup.addressSpaceId = xmlElement.attribute("address-space").toLower().toStdString(); - } - - if (xmlElement.hasAttribute("offset")) { - registerGroup.offset = xmlElement.attribute("offset").toInt(nullptr, 16); - } - - auto& registers = registerGroup.registersMappedByName; - auto registerNodes = xmlElement.elementsByTagName("register"); - for (int registerIndex = 0; registerIndex < registerNodes.count(); registerIndex++) { - try { - auto reg = TargetDescriptionFile::generateRegisterFromXml( - registerNodes.item(registerIndex).toElement() - ); - registers.insert(std::pair(reg.name, reg)); - - } catch (const Exception& exception) { - Logger::debug("Failed to extract register from register group target description element - " - + exception.getMessage()); - } - } - - return registerGroup; -} - -Register TargetDescriptionFile::generateRegisterFromXml(const QDomElement& xmlElement) { - if ( - !xmlElement.hasAttribute("name") - || !xmlElement.hasAttribute("offset") - || !xmlElement.hasAttribute("size") - ) { - throw Exception("Missing register name/offset/size attribute"); - } - - auto reg = Register(); - reg.name = xmlElement.attribute("name").toLower().toStdString(); - - if (reg.name.empty()) { - throw Exception("Empty register name"); - } - - if (xmlElement.hasAttribute("caption")) { - reg.caption = xmlElement.attribute("caption").toStdString(); - } - - if (xmlElement.hasAttribute("ocd-rw")) { - reg.readWriteAccess = xmlElement.attribute("ocd-rw").toLower().toStdString(); - - } else if (xmlElement.hasAttribute("rw")) { - reg.readWriteAccess = xmlElement.attribute("rw").toLower().toStdString(); - } - - bool conversionStatus; - reg.size = xmlElement.attribute("size").toUShort(nullptr, 10); - reg.offset = xmlElement.attribute("offset").toUShort(&conversionStatus, 16); - - if (!conversionStatus) { - // Failed to convert offset hex value as string to uint16_t - throw Exception("Invalid register offset"); - } - - return reg; -} - -void TargetDescriptionFile::loadAddressSpaces() { - - auto addressSpaceNodes = this->deviceElement.elementsByTagName("address-spaces").item(0).toElement() - .elementsByTagName("address-space"); - - for (int addressSpaceIndex = 0; addressSpaceIndex < addressSpaceNodes.count(); addressSpaceIndex++) { - try { - auto addressSpace = TargetDescriptionFile::generateAddressSpaceFromXml( - addressSpaceNodes.item(addressSpaceIndex).toElement() - ); - this->addressSpacesMappedById.insert(std::pair(addressSpace.id, addressSpace)); - - } catch (const Exception& exception) { - Logger::debug("Failed to extract address space from target description element - " + exception.getMessage()); - } - } -} - -void TargetDescriptionFile::loadPropertyGroups() { - if (!this->deviceElement.isElement()) { - throw TargetDescriptionParsingFailureException("Device element not found."); - } - - auto propertyGroupNodes = this->deviceElement.elementsByTagName("property-groups").item(0).toElement() - .elementsByTagName("property-group"); - - for (int propertyGroupIndex = 0; propertyGroupIndex < propertyGroupNodes.count(); propertyGroupIndex++) { - auto propertyGroupElement = propertyGroupNodes.item(propertyGroupIndex).toElement(); - auto propertyGroupName = propertyGroupElement.attributes().namedItem("name").nodeValue().toLower().toStdString(); - PropertyGroup propertyGroup; - propertyGroup.name = propertyGroupName; - - auto propertyNodes = propertyGroupElement.elementsByTagName("property"); - for (int propertyIndex = 0; propertyIndex < propertyNodes.count(); propertyIndex++) { - auto propertyElement = propertyNodes.item(propertyIndex).toElement(); - auto propertyName = propertyElement.attributes().namedItem("name").nodeValue(); - - Property property; - property.name = propertyName.toStdString(); - property.value = propertyElement.attributes().namedItem("value").nodeValue(); - - propertyGroup.propertiesMappedByName.insert(std::pair(propertyName.toLower().toStdString(), property)); - } - - this->propertyGroupsMappedByName.insert(std::pair(propertyGroup.name, propertyGroup)); - } -} - -void TargetDescriptionFile::loadModules() { - auto moduleNodes = this->xml.elementsByTagName("modules").item(0).toElement() - .elementsByTagName("module"); - - for (int moduleIndex = 0; moduleIndex < moduleNodes.count(); moduleIndex++) { - auto moduleElement = moduleNodes.item(moduleIndex).toElement(); - auto moduleName = moduleElement.attributes().namedItem("name").nodeValue().toLower().toStdString(); - Module module; - module.name = moduleName; - - auto registerGroupNodes = moduleElement.elementsByTagName("register-group"); - for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) { - auto registerGroup = TargetDescriptionFile::generateRegisterGroupFromXml( - registerGroupNodes.item(registerGroupIndex).toElement() - ); - - module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup)); - } - - this->modulesMappedByName.insert(std::pair(module.name, module)); - } -} - -void TargetDescriptionFile::loadPeripheralModules() { - auto moduleNodes = this->deviceElement.elementsByTagName("peripherals").item(0).toElement() - .elementsByTagName("module"); - - for (int moduleIndex = 0; moduleIndex < moduleNodes.count(); moduleIndex++) { - auto moduleElement = moduleNodes.item(moduleIndex).toElement(); - auto moduleName = moduleElement.attributes().namedItem("name").nodeValue().toLower().toStdString(); - Module module; - module.name = moduleName; - - auto registerGroupNodes = moduleElement.elementsByTagName("register-group"); - for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) { - auto registerGroup = TargetDescriptionFile::generateRegisterGroupFromXml( - registerGroupNodes.item(registerGroupIndex).toElement() - ); - - module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup)); - - if (registerGroup.moduleName.has_value()) { - this->peripheralRegisterGroupsMappedByModuleRegisterGroupName[registerGroup.moduleName.value()] - .emplace_back(registerGroup); + if (!conversionStatus) { + // Failed to convert size hex value as string to uint16_t + throw Exception("Invalid size"); } } - auto instanceNodes = moduleElement.elementsByTagName("instance"); - for (int instanceIndex = 0; instanceIndex < instanceNodes.count(); instanceIndex++) { - auto instanceXml = instanceNodes.item(instanceIndex).toElement(); - auto instance = ModuleInstance(); - instance.name = instanceXml.attribute("name").toLower().toStdString(); + return segment; + } - auto registerGroupNodes = instanceXml.elementsByTagName("register-group"); + RegisterGroup TargetDescriptionFile::generateRegisterGroupFromXml(const QDomElement& xmlElement) { + if (!xmlElement.hasAttribute("name")) { + throw Exception("Missing register group name attribute"); + } + + auto registerGroup = RegisterGroup(); + registerGroup.name = xmlElement.attribute("name").toLower().toStdString(); + + if (registerGroup.name.empty()) { + throw Exception("Empty register group name"); + } + + if (xmlElement.hasAttribute("name-in-module")) { + registerGroup.moduleName = xmlElement.attribute("name-in-module").toLower().toStdString(); + } + + if (xmlElement.hasAttribute("address-space")) { + registerGroup.addressSpaceId = xmlElement.attribute("address-space").toLower().toStdString(); + } + + if (xmlElement.hasAttribute("offset")) { + registerGroup.offset = xmlElement.attribute("offset").toInt(nullptr, 16); + } + + auto& registers = registerGroup.registersMappedByName; + auto registerNodes = xmlElement.elementsByTagName("register"); + for (int registerIndex = 0; registerIndex < registerNodes.count(); registerIndex++) { + try { + auto reg = TargetDescriptionFile::generateRegisterFromXml( + registerNodes.item(registerIndex).toElement() + ); + registers.insert(std::pair(reg.name, reg)); + + } catch (const Exception& exception) { + Logger::debug("Failed to extract register from register group target description element - " + + exception.getMessage()); + } + } + + return registerGroup; + } + + Register TargetDescriptionFile::generateRegisterFromXml(const QDomElement& xmlElement) { + if ( + !xmlElement.hasAttribute("name") + || !xmlElement.hasAttribute("offset") + || !xmlElement.hasAttribute("size") + ) { + throw Exception("Missing register name/offset/size attribute"); + } + + auto reg = Register(); + reg.name = xmlElement.attribute("name").toLower().toStdString(); + + if (reg.name.empty()) { + throw Exception("Empty register name"); + } + + if (xmlElement.hasAttribute("caption")) { + reg.caption = xmlElement.attribute("caption").toStdString(); + } + + if (xmlElement.hasAttribute("ocd-rw")) { + reg.readWriteAccess = xmlElement.attribute("ocd-rw").toLower().toStdString(); + + } else if (xmlElement.hasAttribute("rw")) { + reg.readWriteAccess = xmlElement.attribute("rw").toLower().toStdString(); + } + + bool conversionStatus = false; + reg.size = xmlElement.attribute("size").toUShort(nullptr, 10); + reg.offset = xmlElement.attribute("offset").toUShort(&conversionStatus, 16); + + if (!conversionStatus) { + // Failed to convert offset hex value as string to uint16_t + throw Exception("Invalid register offset"); + } + + return reg; + } + + void TargetDescriptionFile::loadAddressSpaces() { + + auto addressSpaceNodes = this->deviceElement.elementsByTagName("address-spaces").item(0).toElement() + .elementsByTagName("address-space"); + + for (int addressSpaceIndex = 0; addressSpaceIndex < addressSpaceNodes.count(); addressSpaceIndex++) { + try { + auto addressSpace = TargetDescriptionFile::generateAddressSpaceFromXml( + addressSpaceNodes.item(addressSpaceIndex).toElement() + ); + this->addressSpacesMappedById.insert(std::pair(addressSpace.id, addressSpace)); + + } catch (const Exception& exception) { + Logger::debug( + "Failed to extract address space from target description element - " + + exception.getMessage() + ); + } + } + } + + void TargetDescriptionFile::loadPropertyGroups() { + if (!this->deviceElement.isElement()) { + throw TargetDescriptionParsingFailureException("Device element not found."); + } + + auto propertyGroupNodes = this->deviceElement.elementsByTagName("property-groups").item(0).toElement() + .elementsByTagName("property-group"); + + for (int propertyGroupIndex = 0; propertyGroupIndex < propertyGroupNodes.count(); propertyGroupIndex++) { + auto propertyGroupElement = propertyGroupNodes.item(propertyGroupIndex).toElement(); + auto propertyGroupName = propertyGroupElement.attributes().namedItem( + "name" + ).nodeValue().toLower().toStdString(); + PropertyGroup propertyGroup; + propertyGroup.name = propertyGroupName; + + auto propertyNodes = propertyGroupElement.elementsByTagName("property"); + for (int propertyIndex = 0; propertyIndex < propertyNodes.count(); propertyIndex++) { + auto propertyElement = propertyNodes.item(propertyIndex).toElement(); + auto propertyName = propertyElement.attributes().namedItem("name").nodeValue(); + + Property property; + property.name = propertyName.toStdString(); + property.value = propertyElement.attributes().namedItem("value").nodeValue(); + + propertyGroup.propertiesMappedByName.insert( + std::pair(propertyName.toLower().toStdString(), property) + ); + } + + this->propertyGroupsMappedByName.insert(std::pair(propertyGroup.name, propertyGroup)); + } + } + + void TargetDescriptionFile::loadModules() { + auto moduleNodes = this->xml.elementsByTagName("modules").item(0).toElement() + .elementsByTagName("module"); + + for (int moduleIndex = 0; moduleIndex < moduleNodes.count(); moduleIndex++) { + auto moduleElement = moduleNodes.item(moduleIndex).toElement(); + auto moduleName = moduleElement.attributes().namedItem("name").nodeValue().toLower().toStdString(); + Module module; + module.name = moduleName; + + auto registerGroupNodes = moduleElement.elementsByTagName("register-group"); for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) { auto registerGroup = TargetDescriptionFile::generateRegisterGroupFromXml( registerGroupNodes.item(registerGroupIndex).toElement() ); - instance.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup)); + module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup)); } - auto signalNodes = instanceXml.elementsByTagName("signals").item(0).toElement() - .elementsByTagName("signal"); - for (int signalIndex = 0; signalIndex < signalNodes.count(); signalIndex++) { - auto signalXml = signalNodes.item(signalIndex).toElement(); - auto signal = Signal(); - - if (!signalXml.hasAttribute("pad")) { - continue; - } - - signal.padName = signalXml.attribute("pad").toLower().toStdString(); - signal.function = signalXml.attribute("function").toStdString(); - signal.group = signalXml.attribute("group").toStdString(); - auto indexAttribute = signalXml.attribute("index"); - bool indexValid = false; - auto indexValue = indexAttribute.toInt(&indexValid, 10); - - if (!indexAttribute.isEmpty() && indexValid) { - signal.index = indexValue; - } - - instance.instanceSignals.emplace_back(signal); - } - - module.instancesMappedByName.insert(std::pair(instance.name, instance)); + this->modulesMappedByName.insert(std::pair(module.name, module)); } - - this->peripheralModulesMappedByName.insert(std::pair(module.name, module)); } -} -void TargetDescriptionFile::loadVariants() { - auto variantNodes = this->xml.elementsByTagName("variants").item(0).toElement() - .elementsByTagName("variant"); + void TargetDescriptionFile::loadPeripheralModules() { + auto moduleNodes = this->deviceElement.elementsByTagName("peripherals").item(0).toElement() + .elementsByTagName("module"); - for (int variantIndex = 0; variantIndex < variantNodes.count(); variantIndex++) { - try { - auto variantXml = variantNodes.item(variantIndex).toElement(); + for (int moduleIndex = 0; moduleIndex < moduleNodes.count(); moduleIndex++) { + auto moduleElement = moduleNodes.item(moduleIndex).toElement(); + auto moduleName = moduleElement.attributes().namedItem("name").nodeValue().toLower().toStdString(); + Module module; + module.name = moduleName; - if (!variantXml.hasAttribute("ordercode")) { - throw Exception("Missing ordercode attribute"); + auto registerGroupNodes = moduleElement.elementsByTagName("register-group"); + for (int registerGroupIndex = 0; registerGroupIndex < registerGroupNodes.count(); registerGroupIndex++) { + auto registerGroup = TargetDescriptionFile::generateRegisterGroupFromXml( + registerGroupNodes.item(registerGroupIndex).toElement() + ); + + module.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup)); + + if (registerGroup.moduleName.has_value()) { + this->peripheralRegisterGroupsMappedByModuleRegisterGroupName[registerGroup.moduleName.value()] + .emplace_back(registerGroup); + } } - if (!variantXml.hasAttribute("package")) { - throw Exception("Missing package attribute"); + auto instanceNodes = moduleElement.elementsByTagName("instance"); + for (int instanceIndex = 0; instanceIndex < instanceNodes.count(); instanceIndex++) { + auto instanceXml = instanceNodes.item(instanceIndex).toElement(); + auto instance = ModuleInstance(); + instance.name = instanceXml.attribute("name").toLower().toStdString(); + + auto registerGroupNodes = instanceXml.elementsByTagName("register-group"); + for ( + int registerGroupIndex = 0; + registerGroupIndex < registerGroupNodes.count(); + registerGroupIndex++ + ) { + auto registerGroup = TargetDescriptionFile::generateRegisterGroupFromXml( + registerGroupNodes.item(registerGroupIndex).toElement() + ); + + instance.registerGroupsMappedByName.insert(std::pair(registerGroup.name, registerGroup)); + } + + auto signalNodes = instanceXml.elementsByTagName("signals").item(0).toElement() + .elementsByTagName("signal"); + for (int signalIndex = 0; signalIndex < signalNodes.count(); signalIndex++) { + auto signalXml = signalNodes.item(signalIndex).toElement(); + auto signal = Signal(); + + if (!signalXml.hasAttribute("pad")) { + continue; + } + + signal.padName = signalXml.attribute("pad").toLower().toStdString(); + signal.function = signalXml.attribute("function").toStdString(); + signal.group = signalXml.attribute("group").toStdString(); + auto indexAttribute = signalXml.attribute("index"); + bool indexValid = false; + auto indexValue = indexAttribute.toInt(&indexValid, 10); + + if (!indexAttribute.isEmpty() && indexValid) { + signal.index = indexValue; + } + + instance.instanceSignals.emplace_back(signal); + } + + module.instancesMappedByName.insert(std::pair(instance.name, instance)); } - if (!variantXml.hasAttribute("pinout")) { - throw Exception("Missing pinout attribute"); + this->peripheralModulesMappedByName.insert(std::pair(module.name, module)); + } + } + + void TargetDescriptionFile::loadVariants() { + auto variantNodes = this->xml.elementsByTagName("variants").item(0).toElement() + .elementsByTagName("variant"); + + for (int variantIndex = 0; variantIndex < variantNodes.count(); variantIndex++) { + try { + auto variantXml = variantNodes.item(variantIndex).toElement(); + + if (!variantXml.hasAttribute("ordercode")) { + throw Exception("Missing ordercode attribute"); + } + + if (!variantXml.hasAttribute("package")) { + throw Exception("Missing package attribute"); + } + + if (!variantXml.hasAttribute("pinout")) { + throw Exception("Missing pinout attribute"); + } + + auto variant = Variant(); + variant.name = variantXml.attribute("ordercode").toStdString(); + variant.pinoutName = variantXml.attribute("pinout").toLower().toStdString(); + variant.package = variantXml.attribute("package").toUpper().toStdString(); + + if (variantXml.hasAttribute("disabled")) { + variant.disabled = (variantXml.attribute("disabled") == "1"); + } + + this->variants.push_back(variant); + + } catch (const Exception& exception) { + Logger::debug( + "Failed to extract variant from target description element - " + exception.getMessage() + ); } + } + } - auto variant = Variant(); - variant.name = variantXml.attribute("ordercode").toStdString(); - variant.pinoutName = variantXml.attribute("pinout").toLower().toStdString(); - variant.package = variantXml.attribute("package").toUpper().toStdString(); + void TargetDescriptionFile::loadPinouts() { + auto pinoutNodes = this->xml.elementsByTagName("pinouts").item(0).toElement() + .elementsByTagName("pinout"); - if (variantXml.hasAttribute("disabled")) { - variant.disabled = (variantXml.attribute("disabled") == "1"); + for (int pinoutIndex = 0; pinoutIndex < pinoutNodes.count(); pinoutIndex++) { + try { + auto pinoutXml = pinoutNodes.item(pinoutIndex).toElement(); + + if (!pinoutXml.hasAttribute("name")) { + throw Exception("Missing name attribute"); + } + + auto pinout = Pinout(); + pinout.name = pinoutXml.attribute("name").toLower().toStdString(); + + auto pinNodes = pinoutXml.elementsByTagName("pin"); + + for (int pinIndex = 0; pinIndex < pinNodes.count(); pinIndex++) { + auto pinXml = pinNodes.item(pinIndex).toElement(); + + if (!pinXml.hasAttribute("position")) { + throw Exception( + "Missing position attribute on pin element " + std::to_string(pinIndex) + ); + } + + if (!pinXml.hasAttribute("pad")) { + throw Exception("Missing pad attribute on pin element " + std::to_string(pinIndex)); + } + + auto pin = Pin(); + bool positionConversionSucceeded = true; + pin.position = pinXml.attribute("position").toInt(&positionConversionSucceeded, 10); + pin.pad = pinXml.attribute("pad").toLower().toStdString(); + + if (!positionConversionSucceeded) { + throw Exception("Failed to convert position attribute value to integer on pin element " + + std::to_string(pinIndex)); + } + + pinout.pins.push_back(pin); + } + + this->pinoutsMappedByName.insert(std::pair(pinout.name, pinout)); + + } catch (const Exception& exception) { + Logger::debug( + "Failed to extract pinout from target description element - " + exception.getMessage() + ); } + } + } - this->variants.push_back(variant); + void TargetDescriptionFile::loadInterfaces() { + auto interfaceNodes = this->deviceElement.elementsByTagName("interfaces").item(0).toElement() + .elementsByTagName("interface"); - } catch (const Exception& exception) { - Logger::debug("Failed to extract variant from target description element - " + exception.getMessage()); - } - } -} - -void TargetDescriptionFile::loadPinouts() { - auto pinoutNodes = this->xml.elementsByTagName("pinouts").item(0).toElement() - .elementsByTagName("pinout"); - - for (int pinoutIndex = 0; pinoutIndex < pinoutNodes.count(); pinoutIndex++) { - try { - auto pinoutXml = pinoutNodes.item(pinoutIndex).toElement(); - - if (!pinoutXml.hasAttribute("name")) { - throw Exception("Missing name attribute"); - } - - auto pinout = Pinout(); - pinout.name = pinoutXml.attribute("name").toLower().toStdString(); - - auto pinNodes = pinoutXml.elementsByTagName("pin"); - - for (int pinIndex = 0; pinIndex < pinNodes.count(); pinIndex++) { - auto pinXml = pinNodes.item(pinIndex).toElement(); - - if (!pinXml.hasAttribute("position")) { - throw Exception("Missing position attribute on pin element " + std::to_string(pinIndex)); - } - - if (!pinXml.hasAttribute("pad")) { - throw Exception("Missing pad attribute on pin element " + std::to_string(pinIndex)); - } - - auto pin = Pin(); - bool positionConversionSucceeded = true; - pin.position = pinXml.attribute("position").toInt(&positionConversionSucceeded, 10); - pin.pad = pinXml.attribute("pad").toLower().toStdString(); - - if (!positionConversionSucceeded) { - throw Exception("Failed to convert position attribute value to integer on pin element " - + std::to_string(pinIndex)); - } - - pinout.pins.push_back(pin); - } - - this->pinoutsMappedByName.insert(std::pair(pinout.name, pinout)); - - } catch (const Exception& exception) { - Logger::debug("Failed to extract pinout from target description element - " + exception.getMessage()); - } - } -} - -void TargetDescriptionFile::loadInterfaces() { - auto interfaceNodes = this->deviceElement.elementsByTagName("interfaces").item(0).toElement() - .elementsByTagName("interface"); - - for (int interfaceIndex = 0; interfaceIndex < interfaceNodes.count(); interfaceIndex++) { - try { - auto interfaceXml = interfaceNodes.item(interfaceIndex).toElement(); - - if (!interfaceXml.hasAttribute("name")) { - throw Exception("Missing name attribute"); - } - - auto interface = Interface(); - interface.name = interfaceXml.attribute("name").toLower().toStdString(); - - if (interfaceXml.hasAttribute("type")) { - interface.type = interfaceXml.attribute("type").toStdString(); - } - - this->interfacesByName.insert(std::pair(interface.name, interface)); - - } catch (const Exception& exception) { - Logger::debug("Failed to extract interface from target description element - " + exception.getMessage()); + for (int interfaceIndex = 0; interfaceIndex < interfaceNodes.count(); interfaceIndex++) { + try { + auto interfaceXml = interfaceNodes.item(interfaceIndex).toElement(); + + if (!interfaceXml.hasAttribute("name")) { + throw Exception("Missing name attribute"); + } + + auto interface = Interface(); + interface.name = interfaceXml.attribute("name").toLower().toStdString(); + + if (interfaceXml.hasAttribute("type")) { + interface.type = interfaceXml.attribute("type").toStdString(); + } + + this->interfacesByName.insert(std::pair(interface.name, interface)); + + } catch (const Exception& exception) { + Logger::debug( + "Failed to extract interface from target description element - " + exception.getMessage() + ); + } } } } diff --git a/src/Targets/TargetRegister.cpp b/src/Targets/TargetRegister.cpp index 8841d9ec..eab50eeb 100644 --- a/src/Targets/TargetRegister.cpp +++ b/src/Targets/TargetRegister.cpp @@ -1,14 +1,15 @@ #include "TargetRegister.hpp" -using namespace Bloom::Targets; +namespace Bloom::Targets +{ + std::size_t TargetRegisterDescriptor::getHash() const { + if (!this->cachedHash.has_value()) { + auto stringHasher = std::hash(); -std::size_t TargetRegisterDescriptor::getHash() const { - if (!this->cachedHash.has_value()) { - auto stringHasher = std::hash(); + this->cachedHash = stringHasher(std::to_string(this->startAddress.value_or(0)) + + "_" + std::to_string(static_cast(this->type))); + } - this->cachedHash = stringHasher(std::to_string(this->startAddress.value_or(0)) - + "_" + std::to_string(static_cast(this->type))); + return this->cachedHash.value(); } - - return this->cachedHash.value(); } diff --git a/src/VersionNumber.cpp b/src/VersionNumber.cpp index 68137713..7c301d5e 100644 --- a/src/VersionNumber.cpp +++ b/src/VersionNumber.cpp @@ -3,26 +3,27 @@ #include #include -using namespace Bloom; +namespace Bloom +{ + VersionNumber::VersionNumber(std::uint16_t major, std::uint16_t minor, std::uint16_t patch) + : major{major}, minor{minor}, patch{patch} { + this->combined = static_cast( + std::stoul(std::to_string(this->major) + std::to_string(this->minor) + std::to_string(this->patch)) + ); + } -VersionNumber::VersionNumber(std::uint16_t major, std::uint16_t minor, std::uint16_t patch) -: major{major}, minor{minor}, patch{patch} { - this->combined = static_cast( - std::stoul(std::to_string(this->major) + std::to_string(this->minor) + std::to_string(this->patch)) - ); -} - -VersionNumber::VersionNumber(const std::string& versionNumber) { - auto versionNumberQStr = QString::fromStdString(versionNumber); - const auto explodedString = versionNumberQStr.split('.'); - - this->major = explodedString.value(0, "0").toUShort(); - this->minor = explodedString.value(1, "0").toUShort(); - this->patch = explodedString.value(2, "0").toUShort(); - - this->combined = versionNumberQStr.remove('.').toUInt(); -} - -std::string VersionNumber::toString() const { - return std::to_string(this->major) + "." + std::to_string(this->minor) + "." + std::to_string(this->patch); + VersionNumber::VersionNumber(const std::string& versionNumber) { + auto versionNumberQStr = QString::fromStdString(versionNumber); + const auto explodedString = versionNumberQStr.split('.'); + + this->major = explodedString.value(0, "0").toUShort(); + this->minor = explodedString.value(1, "0").toUShort(); + this->patch = explodedString.value(2, "0").toUShort(); + + this->combined = versionNumberQStr.remove('.').toUInt(); + } + + std::string VersionNumber::toString() const { + return std::to_string(this->major) + "." + std::to_string(this->minor) + "." + std::to_string(this->patch); + } } diff --git a/src/main.cpp b/src/main.cpp index 1b66af3c..d8255e57 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,14 +3,12 @@ #include "Application.hpp" -using namespace Bloom; - int main(int argc, char* argv[]) { auto arguments = std::vector(); if (argc > 1) { arguments.assign(argv + 1, argv + argc); } - auto application = Application(); + auto application = Bloom::Application(); return application.run(arguments); }