Removed using namespace directive for class member function definitions in source files
This commit is contained in:
@@ -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<std::string>& arguments) {
|
||||
try {
|
||||
this->setName("Bloom");
|
||||
int Application::run(const std::vector<std::string>& 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<Insight>(
|
||||
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<Insight>(
|
||||
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<Events::ShutdownApplication>(
|
||||
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<Events::TargetControllerThreadStateChanged>(
|
||||
std::bind(&Application::onTargetControllerThreadStateChanged, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
applicationEventListener->registerCallbackForEventType<Events::DebugServerThreadStateChanged>(
|
||||
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<Events::ShutdownApplication>(
|
||||
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<Events::TargetControllerThreadStateChanged>(
|
||||
std::bind(&Application::onTargetControllerThreadStateChanged, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
applicationEventListener->registerCallbackForEventType<Events::DebugServerThreadStateChanged>(
|
||||
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<TargetController>(
|
||||
this->eventManager,
|
||||
this->projectConfig.value(),
|
||||
this->environmentConfig.value()
|
||||
);
|
||||
|
||||
this->targetControllerThread = std::thread(
|
||||
&TargetController::run,
|
||||
this->targetController.get()
|
||||
);
|
||||
|
||||
auto tcStateChangeEvent = this->applicationEventListener->waitForEvent<Events::TargetControllerThreadStateChanged>();
|
||||
|
||||
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<Events::ShutdownTargetController>());
|
||||
this->applicationEventListener->waitForEvent<Events::TargetControllerThreadStateChanged>(
|
||||
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<Events::DebugServerThreadStateChanged>();
|
||||
|
||||
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<Events::ShutdownDebugServer>());
|
||||
this->applicationEventListener->waitForEvent<Events::DebugServerThreadStateChanged>(
|
||||
std::chrono::milliseconds(5000)
|
||||
void Application::startTargetController() {
|
||||
this->targetController = std::make_unique<TargetController>(
|
||||
this->eventManager,
|
||||
this->projectConfig.value(),
|
||||
this->environmentConfig.value()
|
||||
);
|
||||
|
||||
this->targetControllerThread = std::thread(
|
||||
&TargetController::run,
|
||||
this->targetController.get()
|
||||
);
|
||||
|
||||
auto tcStateChangeEvent =
|
||||
this->applicationEventListener->waitForEvent<Events::TargetControllerThreadStateChanged>();
|
||||
|
||||
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<Events::ShutdownTargetController>());
|
||||
this->applicationEventListener->waitForEvent<Events::TargetControllerThreadStateChanged>(
|
||||
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<Events::DebugServerThreadStateChanged>();
|
||||
|
||||
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<Events::ShutdownDebugServer>());
|
||||
this->applicationEventListener->waitForEvent<Events::DebugServerThreadStateChanged>(
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<EventNotifier>();
|
||||
this->eventListener->setInterruptEventNotifier(this->interruptEventNotifier);
|
||||
|
||||
this->interruptEventNotifier = std::make_shared<EventNotifier>();
|
||||
this->eventListener->setInterruptEventNotifier(this->interruptEventNotifier);
|
||||
// Register event handlers
|
||||
this->eventListener->registerCallbackForEventType<Events::ShutdownDebugServer>(
|
||||
std::bind(&DebugServer::onShutdownDebugServerEvent, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
// Register event handlers
|
||||
this->eventListener->registerCallbackForEventType<Events::ShutdownDebugServer>(
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<GdbRegisterNumberType> 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<GdbRegisterNumberType> 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.");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,126 +4,138 @@
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
using namespace Bloom::DebugServers::Gdb;
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
namespace Bloom::DebugServers::Gdb
|
||||
{
|
||||
using CommandPackets::CommandPacket;
|
||||
|
||||
std::vector<std::vector<unsigned char>> CommandPacketFactory::extractRawPackets(std::vector<unsigned char> buffer) {
|
||||
std::vector<std::vector<unsigned char>> output;
|
||||
std::vector<std::vector<unsigned char>> CommandPacketFactory::extractRawPackets(
|
||||
std::vector<unsigned char> buffer
|
||||
) {
|
||||
std::vector<std::vector<unsigned char>> 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<unsigned char> rawPacket;
|
||||
rawPacket.push_back('$');
|
||||
} else if (byte == '$') {
|
||||
// Beginning of packet
|
||||
std::vector<unsigned char> 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<CommandPacket> CommandPacketFactory::create(std::vector<unsigned char> rawPacket) {
|
||||
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
|
||||
// This is an interrupt request - create a fake packet for it
|
||||
return std::make_unique<CommandPackets::InterruptExecution>(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<CommandPackets::SupportedFeaturesQuery>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') {
|
||||
return std::make_unique<CommandPackets::ReadRegisters>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'P') {
|
||||
return std::make_unique<CommandPackets::WriteRegister>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'c') {
|
||||
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 's') {
|
||||
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'm') {
|
||||
return std::make_unique<CommandPackets::ReadMemory>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'M') {
|
||||
return std::make_unique<CommandPackets::WriteMemory>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'Z') {
|
||||
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'z') {
|
||||
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
|
||||
std::unique_ptr<CommandPacket> CommandPacketFactory::create(std::vector<unsigned char> rawPacket) {
|
||||
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
|
||||
// This is an interrupt request - create a fake packet for it
|
||||
return std::make_unique<CommandPackets::InterruptExecution>(rawPacket);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<CommandPacket>(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<CommandPackets::SupportedFeaturesQuery>(rawPacket);
|
||||
}
|
||||
|
||||
if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') {
|
||||
return std::make_unique<CommandPackets::ReadRegisters>(rawPacket);
|
||||
}
|
||||
|
||||
if (rawPacketString[1] == 'P') {
|
||||
return std::make_unique<CommandPackets::WriteRegister>(rawPacket);
|
||||
}
|
||||
|
||||
if (rawPacketString[1] == 'c') {
|
||||
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
|
||||
}
|
||||
|
||||
if (rawPacketString[1] == 's') {
|
||||
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
|
||||
}
|
||||
|
||||
if (rawPacketString[1] == 'm') {
|
||||
return std::make_unique<CommandPackets::ReadMemory>(rawPacket);
|
||||
}
|
||||
|
||||
if (rawPacketString[1] == 'M') {
|
||||
return std::make_unique<CommandPackets::WriteMemory>(rawPacket);
|
||||
}
|
||||
|
||||
if (rawPacketString[1] == 'Z') {
|
||||
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
|
||||
}
|
||||
|
||||
if (rawPacketString[1] == 'z') {
|
||||
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<CommandPacket>(rawPacket);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::uint32_t>(
|
||||
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::uint32_t>(
|
||||
std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<const char*>(this->data.data() + 1),
|
||||
static_cast<int>(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<const char*>(this->data.data() + 1),
|
||||
static_cast<int>(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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<size_t>(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<size_t>(
|
||||
std::stoi(std::string(this->data.begin() + 1, this->data.end()))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<const char*>(this->data.data() + 2),
|
||||
static_cast<int>(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<const char*>(this->data.data() + 2),
|
||||
static_cast<int>(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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,41 @@
|
||||
#include "SetBreakpoint.hpp"
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
#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<const char*>(this->data.data() + 2),
|
||||
static_cast<int>(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<const char*>(this->data.data() + 2),
|
||||
static_cast<int>(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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#include "StepExecution.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#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::uint32_t>(
|
||||
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::uint32_t>(
|
||||
std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
/**
|
||||
* The address from which to begin the step.
|
||||
*/
|
||||
std::optional<size_t> fromProgramCounter;
|
||||
std::optional<std::size_t> fromProgramCounter;
|
||||
|
||||
explicit StepExecution(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
|
||||
init();
|
||||
|
||||
@@ -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<const char*>(this->data.data() + 11),
|
||||
static_cast<int>(this->data.size() - 11)
|
||||
);
|
||||
|
||||
// The "qSupported:" prefix occupies 11 bytes
|
||||
if (data.size() > 11) {
|
||||
auto packetData = QString::fromLocal8Bit(
|
||||
reinterpret_cast<const char*>(this->data.data() + 11),
|
||||
static_cast<int>(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<decltype(feature)::value_type>(feature.value()));
|
||||
auto feature = gdbFeatureMapping.valueAt(featureString.toStdString());
|
||||
if (feature.has_value()) {
|
||||
this->supportedFeatures.insert(static_cast<decltype(feature)::value_type>(feature.value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<const char*>(this->data.data() + 1),
|
||||
static_cast<int>(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<const char*>(this->data.data() + 1),
|
||||
static_cast<int>(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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<int>(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<int>(packetSegments.front().mid(1).toUInt(nullptr, 16));
|
||||
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
|
||||
std::reverse(this->registerValue.begin(), this->registerValue.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<std::unique_ptr<CommandPacket>> 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<std::unique_ptr<CommandPacket>> 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<std::unique_ptr<CommandPacket>> 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<std::unique_ptr<CommandPacket>> 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<unsigned char> Connection::read(size_t bytes, bool interruptible, std::optional<int> msTimeout) {
|
||||
auto output = std::vector<unsigned char>();
|
||||
constexpr size_t bufferSize = 1024;
|
||||
std::array<unsigned char, bufferSize> 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<struct epoll_event, 1> events = {};
|
||||
std::vector<unsigned char> Connection::read(size_t bytes, bool interruptible, std::optional<int> msTimeout) {
|
||||
auto output = std::vector<unsigned char>();
|
||||
constexpr size_t bufferSize = 1024;
|
||||
std::array<unsigned char, bufferSize> 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<struct epoll_event, 1> 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<unsigned char> 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<unsigned char>& 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<unsigned char> 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<unsigned char>& 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;
|
||||
}
|
||||
|
||||
@@ -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<unsigned char>();
|
||||
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<unsigned char>(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<unsigned char>(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<std::uint16_t>(configPortJsonValue->toString().toInt(nullptr, 10))
|
||||
: static_cast<std::uint16_t>(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<const sockaddr*>(&(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<Events::TargetControllerStateReported>(
|
||||
std::bind(&GdbRspDebugServer::onTargetControllerStateReported, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
|
||||
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<Events::DebugSessionStarted>());
|
||||
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<unsigned char>();
|
||||
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<unsigned char>(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<unsigned char>(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<std::uint16_t>(configPortJsonValue->toString().toInt(nullptr, 10))
|
||||
: static_cast<std::uint16_t>(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<const sockaddr*>(&(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<Events::TargetControllerStateReported>(
|
||||
std::bind(&GdbRspDebugServer::onTargetControllerStateReported, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
|
||||
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<struct epoll_event, maxEvents> 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<Events::DebugSessionStarted>());
|
||||
|
||||
/*
|
||||
* 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<struct epoll_event, maxEvents> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
#include "SupportedFeaturesResponse.hpp"
|
||||
|
||||
using namespace Bloom::DebugServers::Gdb::ResponsePackets;
|
||||
namespace Bloom::DebugServers::Gdb::ResponsePackets
|
||||
{
|
||||
std::vector<unsigned char> SupportedFeaturesResponse::getData() const {
|
||||
std::string output = "qSupported:";
|
||||
auto gdbFeatureMapping = getGdbFeatureToNameMapping();
|
||||
|
||||
std::vector<unsigned char> 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<unsigned char>(output.begin(), output.end());
|
||||
return std::vector<unsigned char>(output.begin(), output.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<EdbgAvr8Interface>(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<EdbgAvr8Interface>(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;
|
||||
}
|
||||
|
||||
@@ -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<EdbgAvr8Interface>(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<EdbgAvr8Interface>(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;
|
||||
}
|
||||
|
||||
@@ -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<EdbgAvr8Interface>(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<EdbgAvr8Interface>(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;
|
||||
}
|
||||
|
||||
@@ -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<EdbgAvr8Interface>(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<EdbgAvr8Interface>(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;
|
||||
}
|
||||
|
||||
@@ -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<EdbgAvr8Interface>(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<EdbgAvr8Interface>(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;
|
||||
}
|
||||
|
||||
@@ -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<EdbgAvr8Interface>(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<EdbgAvr8Interface>(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;
|
||||
}
|
||||
|
||||
@@ -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<EdbgAvr8Interface>(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<EdbgAvr8Interface>(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;
|
||||
}
|
||||
|
||||
@@ -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<EdbgAvr8Interface>(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<EdbgAvr8Interface>(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;
|
||||
}
|
||||
|
||||
@@ -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<milliseconds>(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<milliseconds>(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<std::vector<unsigned char>>(cmsisDapCommand));
|
||||
}
|
||||
|
||||
this->getUsbHidInterface().write(static_cast<std::vector<unsigned char>>(cmsisDapCommand));
|
||||
}
|
||||
std::unique_ptr<Response> CmsisDapInterface::getResponse() {
|
||||
auto rawResponse = this->getUsbHidInterface().read(10000);
|
||||
|
||||
std::unique_ptr<Response> 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());
|
||||
response->init(rawResponse);
|
||||
return response;
|
||||
}
|
||||
|
||||
auto response = std::make_unique<Response>(Response());
|
||||
response->init(rawResponse);
|
||||
return response;
|
||||
}
|
||||
std::unique_ptr<Response> CmsisDapInterface::sendCommandAndWaitForResponse(const Command& cmsisDapCommand) {
|
||||
this->sendCommand(cmsisDapCommand);
|
||||
auto response = this->getResponse();
|
||||
|
||||
std::unique_ptr<Response> 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;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#include "Command.hpp"
|
||||
|
||||
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap;
|
||||
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap
|
||||
{
|
||||
Command::operator std::vector<unsigned char>() const {
|
||||
auto rawCommand = std::vector<unsigned char>(1, this->getCommandId());
|
||||
auto commandData = this->getData();
|
||||
rawCommand.insert(rawCommand.end(), commandData.begin(), commandData.end());
|
||||
|
||||
Command::operator std::vector<unsigned char> () const {
|
||||
auto rawCommand = std::vector<unsigned char>(1, this->getCommandId());
|
||||
auto commandData = this->getData();
|
||||
rawCommand.insert(rawCommand.end(), commandData.begin(), commandData.end());
|
||||
|
||||
return rawCommand;
|
||||
return rawCommand;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<unsigned char>& rawResponse) {
|
||||
if (rawResponse.empty()) {
|
||||
throw Exceptions::Exception("Failed to process CMSIS-DAP response - invalid response");
|
||||
}
|
||||
|
||||
void Response::init(const std::vector<unsigned char>& rawResponse) {
|
||||
if (rawResponse.empty()) {
|
||||
throw Exceptions::Exception("Failed to process CMSIS-DAP response - invalid response");
|
||||
this->setResponseId(rawResponse[0]);
|
||||
this->setData(std::vector<unsigned char>(rawResponse.begin() + 1, rawResponse.end()));
|
||||
}
|
||||
|
||||
this->setResponseId(rawResponse[0]);
|
||||
this->setData(std::vector<unsigned char>(rawResponse.begin() + 1, rawResponse.end()));
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
#include "AvrCommand.hpp"
|
||||
|
||||
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
|
||||
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
||||
{
|
||||
std::vector<unsigned char> AvrCommand::getData() const {
|
||||
std::vector<unsigned char> data;
|
||||
auto commandPacket = this->getCommandPacket();
|
||||
std::size_t commandPacketSize = commandPacket.size();
|
||||
data.resize(3 + commandPacketSize);
|
||||
// FragmentInfo byte
|
||||
data[0] = static_cast<unsigned char>((this->getFragmentNumber() << 4) | this->getFragmentCount());
|
||||
|
||||
std::vector<unsigned char> AvrCommand::getData() const {
|
||||
std::vector<unsigned char> data;
|
||||
auto commandPacket = this->getCommandPacket();
|
||||
std::size_t commandPacketSize = commandPacket.size();
|
||||
data.resize(3 + commandPacketSize);
|
||||
// FragmentInfo byte
|
||||
data[0] = static_cast<unsigned char>((this->getFragmentNumber() << 4) | this->getFragmentCount());
|
||||
// Size byte
|
||||
data[1] = static_cast<unsigned char>(commandPacketSize >> 8);
|
||||
data[2] = static_cast<unsigned char>(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;
|
||||
}
|
||||
|
||||
@@ -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<unsigned char>& rawResponse) {
|
||||
Response::init(rawResponse);
|
||||
void AvrEvent::init(const std::vector<unsigned char>& 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<std::size_t>((responseData[0] << 8) | responseData[1]);
|
||||
// Response size is two bytes, MSB
|
||||
auto responsePacketSize = static_cast<std::size_t>((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<unsigned char>();
|
||||
auto eventData = std::vector<unsigned char>();
|
||||
|
||||
/*
|
||||
* 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<long>(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<long>(responsePacketSize)
|
||||
);
|
||||
|
||||
this->setEventData(eventData);
|
||||
this->setEventData(eventData);
|
||||
|
||||
if (!eventData.empty()) {
|
||||
this->eventId = eventData[0];
|
||||
if (!eventData.empty()) {
|
||||
this->eventId = eventData[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<unsigned char>& rawResponse) {
|
||||
Response::init(rawResponse);
|
||||
void AvrResponse::init(const std::vector<unsigned char>& 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<std::uint8_t>(responseData[0] & 0x0FU));
|
||||
this->setFragmentNumber(static_cast<std::uint8_t>(responseData[0] >> 4));
|
||||
|
||||
// Response size is two bytes, MSB
|
||||
const auto responsePacketSize = static_cast<std::size_t>((responseData[1] << 8U) + responseData[2]);
|
||||
std::vector<unsigned char> 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<std::uint8_t>(responseData[0] & 0x0Fu));
|
||||
this->setFragmentNumber(static_cast<std::uint8_t>(responseData[0] >> 4));
|
||||
|
||||
// Response size is two bytes, MSB
|
||||
std::size_t responsePacketSize = static_cast<std::size_t>((responseData[1] << 8u) + responseData[2]);
|
||||
std::vector<unsigned char> responsePacket;
|
||||
responsePacket.resize(responsePacketSize);
|
||||
|
||||
for (std::size_t i = 0; i < responsePacketSize; i++) {
|
||||
responsePacket[i] = responseData[i + 3];
|
||||
}
|
||||
|
||||
this->setResponsePacket(responsePacket);
|
||||
}
|
||||
|
||||
@@ -3,54 +3,55 @@
|
||||
#include <bitset>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames::Avr8Generic;
|
||||
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
std::vector<unsigned char> 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<unsigned char>(11, 0x00);
|
||||
output[0] = this->excludedAddresses.empty() ? 0x21 : 0x22;
|
||||
output[1] = 0x00;
|
||||
output[2] = static_cast<unsigned char>(this->type);
|
||||
output[3] = static_cast<unsigned char>(this->address);
|
||||
output[4] = static_cast<unsigned char>(this->address >> 8);
|
||||
output[5] = static_cast<unsigned char>(this->address >> 16);
|
||||
output[6] = static_cast<unsigned char>(this->address >> 24);
|
||||
output[7] = static_cast<unsigned char>(this->bytes);
|
||||
output[8] = static_cast<unsigned char>(this->bytes >> 8);
|
||||
output[9] = static_cast<unsigned char>(this->bytes >> 16);
|
||||
output[10] = static_cast<unsigned char>(this->bytes >> 24);
|
||||
|
||||
std::vector<unsigned char> 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<unsigned char>(11, 0x00);
|
||||
output[0] = this->excludedAddresses.empty() ? 0x21 : 0x22;
|
||||
output[1] = 0x00;
|
||||
output[2] = static_cast<unsigned char>(this->type);
|
||||
output[3] = static_cast<unsigned char>(this->address);
|
||||
output[4] = static_cast<unsigned char>(this->address >> 8);
|
||||
output[5] = static_cast<unsigned char>(this->address >> 16);
|
||||
output[6] = static_cast<unsigned char>(this->address >> 24);
|
||||
output[7] = static_cast<unsigned char>(this->bytes);
|
||||
output[8] = static_cast<unsigned char>(this->bytes >> 8);
|
||||
output[9] = static_cast<unsigned char>(this->bytes >> 16);
|
||||
output[10] = static_cast<unsigned char>(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<unsigned char>::digits;
|
||||
auto byteBitset = std::bitset<byteBitSize>();
|
||||
byteBitset.reset();
|
||||
|
||||
constexpr auto byteBitSize = std::numeric_limits<unsigned char>::digits;
|
||||
auto byteBitset = std::bitset<byteBitSize>();
|
||||
byteBitset.reset();
|
||||
for (std::uint32_t address = this->address; address <= endAddress; address++) {
|
||||
auto addressIndex = address - this->address;
|
||||
auto bitIndex = static_cast<std::size_t>(
|
||||
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<std::size_t>(
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,62 +2,63 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
|
||||
|
||||
std::vector<AvrCommand> AvrCommandFrame::generateAvrCommands(std::size_t maximumCommandPacketSize) const {
|
||||
auto rawCommandFrame = static_cast<std::vector<unsigned char>>(*this);
|
||||
std::size_t commandFrameSize = rawCommandFrame.size();
|
||||
auto commandsRequired = static_cast<std::size_t>(
|
||||
ceil(static_cast<float>(commandFrameSize) / static_cast<float>(maximumCommandPacketSize))
|
||||
);
|
||||
|
||||
std::vector<AvrCommand> 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<long>(copiedPacketSize),
|
||||
rawCommandFrame.begin() + static_cast<long>(copiedPacketSize + commandPacketSize)
|
||||
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
||||
{
|
||||
std::vector<AvrCommand> AvrCommandFrame::generateAvrCommands(std::size_t maximumCommandPacketSize) const {
|
||||
auto rawCommandFrame = static_cast<std::vector<unsigned char>>(*this);
|
||||
std::size_t commandFrameSize = rawCommandFrame.size();
|
||||
auto commandsRequired = static_cast<std::size_t>(
|
||||
ceil(static_cast<float>(commandFrameSize) / static_cast<float>(maximumCommandPacketSize))
|
||||
);
|
||||
|
||||
avrCommand.setCommandPacket(commandPacket);
|
||||
avrCommands.push_back(avrCommand);
|
||||
copiedPacketSize += commandPacketSize;
|
||||
std::vector<AvrCommand> 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<long>(copiedPacketSize),
|
||||
rawCommandFrame.begin() + static_cast<long>(copiedPacketSize + commandPacketSize)
|
||||
);
|
||||
|
||||
avrCommand.setCommandPacket(commandPacket);
|
||||
avrCommands.push_back(avrCommand);
|
||||
copiedPacketSize += commandPacketSize;
|
||||
}
|
||||
|
||||
return avrCommands;
|
||||
}
|
||||
|
||||
return avrCommands;
|
||||
}
|
||||
AvrCommandFrame::operator std::vector<unsigned char>() const {
|
||||
auto data = this->getPayload();
|
||||
auto dataSize = data.size();
|
||||
|
||||
AvrCommandFrame::operator std::vector<unsigned char> () const {
|
||||
auto data = this->getPayload();
|
||||
auto dataSize = data.size();
|
||||
auto rawCommand = std::vector<unsigned char>(5);
|
||||
|
||||
auto rawCommand = std::vector<unsigned char>(5);
|
||||
rawCommand[0] = this->SOF;
|
||||
rawCommand[1] = this->getProtocolVersion();
|
||||
|
||||
rawCommand[0] = this->SOF;
|
||||
rawCommand[1] = this->getProtocolVersion();
|
||||
rawCommand[2] = static_cast<unsigned char>(this->getSequenceId());
|
||||
rawCommand[3] = static_cast<unsigned char>(this->getSequenceId() >> 8);
|
||||
|
||||
rawCommand[2] = static_cast<unsigned char>(this->getSequenceId());
|
||||
rawCommand[3] = static_cast<unsigned char>(this->getSequenceId() >> 8);
|
||||
rawCommand[4] = static_cast<unsigned char>(this->getProtocolHandlerId());
|
||||
|
||||
rawCommand[4] = static_cast<unsigned char>(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;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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<std::uint32_t>(
|
||||
(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<std::uint32_t>((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;
|
||||
}
|
||||
|
||||
@@ -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<AvrResponse>& avrResponses) {
|
||||
// Build a raw frame buffer from the AVRResponse objects and just call initFromRawFrame()
|
||||
std::vector<unsigned char> rawFrame;
|
||||
void AvrResponseFrame::initFromAvrResponses(const std::vector<AvrResponse>& avrResponses) {
|
||||
// Build a raw frame buffer from the AVRResponse objects and just call initFromRawFrame()
|
||||
std::vector<unsigned char> 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<unsigned char>& 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<std::uint16_t>((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<unsigned char>& 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<std::uint16_t>((rawFrame[2] << 8) + rawFrame[1]));
|
||||
this->setProtocolHandlerId(rawFrame[3]);
|
||||
|
||||
auto& payload = this->getPayload();
|
||||
payload.insert(payload.begin(), rawFrame.begin() + 4, rawFrame.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Protocols::CmsisDap::Edbg::Avr::AvrEvent> EdbgInterface::requestAvrEvent() {
|
||||
this->sendCommand(AvrEventCommand());
|
||||
auto cmsisResponse = this->getResponse();
|
||||
std::optional<Protocols::CmsisDap::Edbg::Avr::AvrEvent> 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<Protocols::CmsisDap::Edbg::Avr::AvrResponse> EdbgInterface::requestAvrResponses() {
|
||||
std::vector<Protocols::CmsisDap::Edbg::Avr::AvrResponse> responses;
|
||||
AvrResponseCommand responseCommand;
|
||||
std::vector<Protocols::CmsisDap::Edbg::Avr::AvrResponse> 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<Protocols::CmsisDap::Edbg::Avr::AvrResponse> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<std::size_t>(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<unsigned char> HidInterface::read(unsigned int timeout) {
|
||||
std::vector<unsigned char> 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<unsigned char> 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<int>(timeout))
|
||||
) == -1
|
||||
) {
|
||||
throw DeviceCommunicationFailure("Failed to read from HID device.");
|
||||
}
|
||||
|
||||
return static_cast<std::size_t>(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<std::size_t>(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<unsigned char> HidInterface::read(unsigned int timeout) {
|
||||
std::vector<unsigned char> 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<unsigned char> 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<int>(timeout))
|
||||
) == -1
|
||||
) {
|
||||
throw DeviceCommunicationFailure("Failed to read from HID device.");
|
||||
}
|
||||
|
||||
return static_cast<std::size_t>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,121 +1,123 @@
|
||||
#include "Interface.hpp"
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#include <chrono>
|
||||
|
||||
#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<int>(length),
|
||||
&transferred,
|
||||
static_cast<unsigned int>(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<int>(length),
|
||||
length,
|
||||
&transferred,
|
||||
static_cast<unsigned int>(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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<libusb_device*> UsbDevice::findMatchingDevices(
|
||||
std::optional<std::uint16_t> vendorId, std::optional<std::uint16_t> productId
|
||||
) {
|
||||
auto libUsbContext = this->libUsbContext;
|
||||
libusb_device** devices = nullptr;
|
||||
libusb_device* device;
|
||||
std::vector<libusb_device*> 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<libusb_device*> UsbDevice::findMatchingDevices(
|
||||
std::optional<std::uint16_t> vendorId, std::optional<std::uint16_t> productId
|
||||
) {
|
||||
auto* libUsbContext = this->libUsbContext;
|
||||
libusb_device** devices = nullptr;
|
||||
libusb_device* device;
|
||||
std::vector<libusb_device*> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,98 +2,105 @@
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
using namespace Bloom;
|
||||
using namespace Bloom::Events;
|
||||
namespace Bloom
|
||||
{
|
||||
using namespace Bloom::Events;
|
||||
|
||||
std::set<Events::EventType> 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<Events::EventType> 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<SharedGenericEventPointer> 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<SharedGenericEventPointer> 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<SharedGenericEventPointer> EventListener::getEvents() {
|
||||
auto queueLock = this->eventQueueByEventType.acquireLock();
|
||||
auto& eventQueueByType = this->eventQueueByEventType.getReference();
|
||||
std::vector<SharedGenericEventPointer> 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<SharedGenericEventPointer> EventListener::getEvents() {
|
||||
auto queueLock = this->eventQueueByEventType.acquireLock();
|
||||
auto& eventQueueByType = this->eventQueueByEventType.getReference();
|
||||
std::vector<SharedGenericEventPointer> 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();
|
||||
}
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
#include "EventManager.hpp"
|
||||
|
||||
using namespace Bloom;
|
||||
|
||||
void EventManager::registerListener(std::shared_ptr<EventListener> 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<const Events::Event>& 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<EventListener> 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<const Events::Event>& 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,15 @@
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
using namespace Bloom;
|
||||
namespace Bloom
|
||||
{
|
||||
std::string Paths::applicationDirPath() {
|
||||
auto pathCharArray = std::array<char, PATH_MAX>();
|
||||
|
||||
std::string Paths::applicationDirPath() {
|
||||
auto pathCharArray = std::array<char, PATH_MAX>();
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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<Events::ShutdownApplication>(
|
||||
std::bind(&Insight::onShutdownApplicationEvent, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetControllerThreadStateChanged>(
|
||||
std::bind(&Insight::onTargetControllerThreadStateChangedEvent, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::DebugServerThreadStateChanged>(
|
||||
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<Bloom::Targets::TargetDescriptor>();
|
||||
qRegisterMetaType<Bloom::Targets::TargetPinDescriptor>();
|
||||
qRegisterMetaType<Bloom::Targets::TargetPinState>();
|
||||
qRegisterMetaType<Bloom::Targets::TargetState>();
|
||||
qRegisterMetaType<std::map<int, Bloom::Targets::TargetPinState>>();
|
||||
|
||||
// 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<Events::ShutdownApplication>(
|
||||
std::bind(&Insight::onShutdownApplicationEvent, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetControllerThreadStateChanged>(
|
||||
std::bind(&Insight::onTargetControllerThreadStateChangedEvent, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::DebugServerThreadStateChanged>(
|
||||
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<Bloom::Targets::TargetDescriptor>();
|
||||
qRegisterMetaType<Bloom::Targets::TargetPinDescriptor>();
|
||||
qRegisterMetaType<Bloom::Targets::TargetPinState>();
|
||||
qRegisterMetaType<Bloom::Targets::TargetState>();
|
||||
qRegisterMetaType<std::map<int, Bloom::Targets::TargetPinState>>();
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Events::TargetControllerStateReported>(
|
||||
std::bind(&InsightWorker::onTargetControllerStateReportedEvent, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
|
||||
std::bind(&InsightWorker::onTargetStoppedEvent, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetExecutionResumed>(
|
||||
std::bind(&InsightWorker::onTargetResumedEvent, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::RegistersWrittenToTarget>(
|
||||
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<Events::InsightThreadStateChanged>(ThreadState::READY)
|
||||
);
|
||||
|
||||
emit this->ready();
|
||||
}
|
||||
|
||||
void InsightWorker::requestPinStates(int variantId) {
|
||||
this->targetControllerConsole.requestPinStates(variantId);
|
||||
}
|
||||
|
||||
std::optional<InsightWorkerTask*> InsightWorker::getQueuedTask() {
|
||||
auto task = std::optional<InsightWorkerTask*>();
|
||||
|
||||
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<Events::TargetExecutionResumed>(
|
||||
std::chrono::milliseconds(650)
|
||||
);
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetControllerStateReported>(
|
||||
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<Events::TargetExecutionStopped>(
|
||||
std::bind(&InsightWorker::onTargetStoppedEvent, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetExecutionResumed>(
|
||||
std::bind(&InsightWorker::onTargetResumedEvent, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::RegistersWrittenToTarget>(
|
||||
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<Events::InsightThreadStateChanged>(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<InsightWorkerTask*> InsightWorker::getQueuedTask() {
|
||||
auto task = std::optional<InsightWorkerTask*>();
|
||||
|
||||
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<Events::TargetExecutionResumed>(
|
||||
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<InsightWorkerTask*>();
|
||||
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<InsightWorkerTask*>();
|
||||
|
||||
while ((task = this->getQueuedTask()).has_value()) {
|
||||
task.value()->execute(this->targetControllerConsole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<QLabel*>("version-label");
|
||||
auto versionLabel = this->windowWidget->findChild<QLabel*>("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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<QLabel*>(
|
||||
"error-message-description-label"
|
||||
);
|
||||
this->okButton = this->container->findChild<QPushButton*>("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<QLabel*>("error-message-description-label");
|
||||
this->okButton = this->container->findChild<QPushButton*>("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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,138 +2,139 @@
|
||||
|
||||
#include <QLayout>
|
||||
|
||||
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<QHoverEvent*>(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<QHoverEvent*>(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<QPoint, QPoint> 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<QPoint, QPoint> 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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,60 +2,61 @@
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
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<float>(angle) / 90;
|
||||
angleMultiplier = static_cast<float>(angleMultiplier) - std::floor(angleMultiplier);
|
||||
angle = static_cast<int>(90 * angleMultiplier);
|
||||
}
|
||||
|
||||
auto angleRadians = angle * (M_PI / 180);
|
||||
|
||||
size.setWidth(static_cast<int>(
|
||||
std::cos(angleRadians) * textSize.width()
|
||||
+ std::ceil(std::sin(angleRadians) * textSize.height())
|
||||
))
|
||||
;
|
||||
size.setHeight(static_cast<int>(
|
||||
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<float>(angle) / 90;
|
||||
angleMultiplier = static_cast<float>(angleMultiplier) - std::floor(angleMultiplier);
|
||||
angle = static_cast<int>(90 * angleMultiplier);
|
||||
}
|
||||
|
||||
auto angleRadians = angle * (M_PI / 180);
|
||||
|
||||
size.setWidth(static_cast<int>(
|
||||
std::cos(angleRadians) * textSize.width()
|
||||
+ std::ceil(std::sin(angleRadians) * textSize.height())
|
||||
))
|
||||
;
|
||||
size.setHeight(static_cast<int>(
|
||||
std::sin(angleRadians) * textSize.width()
|
||||
+ std::ceil(std::cos(angleRadians) * textSize.height())
|
||||
));
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,23 @@
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
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<QMenu*>(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<QMenu*>(childEvent->child());
|
||||
if (menuWidget != nullptr && menuWidget != this->menu()) {
|
||||
this->setMenu(menuWidget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,71 +3,76 @@
|
||||
#include <QPainter>
|
||||
#include <cmath>
|
||||
|
||||
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<float>(containerSize.width() / 2)),
|
||||
std::ceil(static_cast<float>(containerSize.height() / 2))
|
||||
);
|
||||
painter.rotate(this->angle);
|
||||
painter.translate(
|
||||
-std::ceil(static_cast<float>(containerSize.width() / 2)),
|
||||
-std::ceil(static_cast<float>(containerSize.height() / 2))
|
||||
);
|
||||
}
|
||||
|
||||
this->renderer.render(&painter, QRectF(
|
||||
std::ceil(static_cast<float>(containerSize.width() - svgSize.width()) / 2 + static_cast<float>(margins.left())),
|
||||
std::ceil(static_cast<float>(containerSize.height() - svgSize.height()) / 2 + static_cast<float>(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<float>(containerSize.width() / 2)),
|
||||
std::ceil(static_cast<float>(containerSize.height() / 2))
|
||||
);
|
||||
painter.rotate(this->angle);
|
||||
painter.translate(
|
||||
-std::ceil(static_cast<float>(containerSize.width() / 2)),
|
||||
-std::ceil(static_cast<float>(containerSize.height() / 2))
|
||||
);
|
||||
}
|
||||
|
||||
this->renderer.render(&painter, QRectF(
|
||||
std::ceil(
|
||||
static_cast<float>(containerSize.width() - svgSize.width()) / 2 + static_cast<float>(margins.left())
|
||||
),
|
||||
std::ceil(
|
||||
static_cast<float>(containerSize.height() - svgSize.height()) / 2 + static_cast<float>(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<std::uint32_t>(startAddress + size - 1)),
|
||||
labelText(std::move(labelText)),
|
||||
position(position),
|
||||
width(static_cast<int>((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<std::uint32_t>(startAddress + size - 1)),
|
||||
labelText(std::move(labelText)),
|
||||
position(position),
|
||||
width(static_cast<int>((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);
|
||||
}
|
||||
|
||||
@@ -1,71 +1,72 @@
|
||||
#include "ByteAddressContainer.hpp"
|
||||
|
||||
using namespace Bloom::Widgets;
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
void ByteAddressContainer::adjustAddressLabels(
|
||||
const std::map<std::size_t, std::vector<ByteItem*>>& byteItemsByRowIndex
|
||||
) {
|
||||
static const auto margins = QMargins(0, 10, 0, 0);
|
||||
|
||||
void ByteAddressContainer::adjustAddressLabels(
|
||||
const std::map<std::size_t, std::vector<ByteItem*>>& byteItemsByRowIndex
|
||||
) {
|
||||
static const auto margins = QMargins(0, 10, 0, 0);
|
||||
const auto newRowCount = byteItemsByRowIndex.size();
|
||||
const auto layoutItemMaxIndex = static_cast<int>(this->addressItemsByRowIndex.size() - 1);
|
||||
|
||||
const auto newRowCount = byteItemsByRowIndex.size();
|
||||
const auto layoutItemMaxIndex = static_cast<int>(this->addressItemsByRowIndex.size() - 1);
|
||||
for (const auto& mappingPair : byteItemsByRowIndex) {
|
||||
const auto rowIndex = static_cast<std::size_t>(mappingPair.first);
|
||||
const auto& byteItems = mappingPair.second;
|
||||
|
||||
for (const auto& mappingPair : byteItemsByRowIndex) {
|
||||
const auto rowIndex = static_cast<std::size_t>(mappingPair.first);
|
||||
const auto& byteItems = mappingPair.second;
|
||||
if (byteItems.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (byteItems.empty()) {
|
||||
continue;
|
||||
ByteAddressItem* addressLabel = nullptr;
|
||||
if (static_cast<int>(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<int>(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<int>(this->boundingRect().height())
|
||||
);
|
||||
|
||||
painter->setPen(borderColor);
|
||||
painter->drawLine(
|
||||
ByteAddressContainer::WIDTH - 1,
|
||||
0,
|
||||
ByteAddressContainer::WIDTH - 1,
|
||||
static_cast<int>(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<int>(this->boundingRect().height())
|
||||
);
|
||||
|
||||
painter->setPen(borderColor);
|
||||
painter->drawLine(
|
||||
ByteAddressContainer::WIDTH - 1,
|
||||
0,
|
||||
ByteAddressContainer::WIDTH - 1,
|
||||
static_cast<int>(this->boundingRect().height())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
#include "ByteAddressItem.hpp"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,166 +1,175 @@
|
||||
#include "ByteItem.hpp"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
|
||||
using namespace Bloom::Widgets;
|
||||
|
||||
ByteItem::ByteItem(
|
||||
std::size_t byteIndex,
|
||||
std::uint32_t address,
|
||||
std::optional<std::uint32_t>& currentStackPointer,
|
||||
std::optional<ByteItem*>& hoveredByteItem,
|
||||
std::optional<AnnotationItem*>& hoveredAnnotationItem,
|
||||
std::set<std::uint32_t>& 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<std::uint32_t>& currentStackPointer,
|
||||
std::optional<ByteItem*>& hoveredByteItem,
|
||||
std::optional<AnnotationItem*>& hoveredAnnotationItem,
|
||||
std::set<std::uint32_t>& 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<QColor>();
|
||||
|
||||
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<QColor>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FocusedMemoryRegion>& focusedMemoryRegions,
|
||||
std::vector<ExcludedMemoryRegion>& 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<FocusedMemoryRegion>& focusedMemoryRegions,
|
||||
std::vector<ExcludedMemoryRegion>& 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,494 +2,497 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
using namespace Bloom::Widgets;
|
||||
|
||||
using Bloom::Targets::TargetMemoryDescriptor;
|
||||
|
||||
ByteItemGraphicsScene::ByteItemGraphicsScene(
|
||||
const TargetMemoryDescriptor& targetMemoryDescriptor,
|
||||
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
|
||||
std::vector<ExcludedMemoryRegion>& 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<FocusedMemoryRegion>& focusedMemoryRegions,
|
||||
std::vector<ExcludedMemoryRegion>& 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<std::uint32_t>& highlightedAddresses) {
|
||||
this->highlightedAddresses = highlightedAddresses;
|
||||
this->invalidateChildItemCaches();
|
||||
}
|
||||
|
||||
void ByteItemGraphicsScene::setHighlightedAddresses(const std::set<std::uint32_t>& 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::size_t>(
|
||||
std::floor(
|
||||
(width - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH
|
||||
+ ByteItem::RIGHT_MARGIN) / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN)
|
||||
)
|
||||
);
|
||||
const auto rowCount = static_cast<int>(
|
||||
std::ceil(static_cast<double>(this->byteItemsByAddress.size()) / static_cast<double>(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<int>(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<int>(
|
||||
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::size_t>(
|
||||
std::floor(
|
||||
(width - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + ByteItem::RIGHT_MARGIN)
|
||||
/ (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN)
|
||||
)
|
||||
);
|
||||
const auto rowCount = static_cast<int>(
|
||||
std::ceil(static_cast<double>(this->byteItemsByAddress.size()) / static_cast<double>(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<int>(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<int>(
|
||||
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<ByteItem*>(hoveredItems.at(0));
|
||||
hoveredAnnotationItem = dynamic_cast<AnnotationItem*>(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::size_t>(
|
||||
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<std::size_t, std::vector<ByteItem*>> byteWidgetsByRowIndex;
|
||||
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByColumnIndex;
|
||||
if (!hoveredItems.empty()) {
|
||||
hoveredByteItem = dynamic_cast<ByteItem*>(hoveredItems.at(0));
|
||||
hoveredAnnotationItem = dynamic_cast<AnnotationItem*>(hoveredItems.at(0));
|
||||
}
|
||||
|
||||
auto rowIndicesWithTopAnnotations = std::set<std::size_t>();
|
||||
auto rowIndicesWithBottomAnnotations = std::set<std::size_t>();
|
||||
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
|
||||
if (hoveredByteItem != nullptr) {
|
||||
this->onByteWidgetEnter(hoveredByteItem);
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto* annotationItem : this->annotationItems) {
|
||||
const auto firstByteRowIndex = static_cast<std::size_t>(
|
||||
std::ceil(static_cast<double>((annotationItem->startAddress - memoryAddressRange.startAddress) + 1)
|
||||
/ static_cast<double>(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::size_t>(
|
||||
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::size_t>(
|
||||
std::ceil(static_cast<double>((annotationItem->endAddress - memoryAddressRange.startAddress) + 1)
|
||||
/ static_cast<double>(columnCount)) - 1
|
||||
);
|
||||
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByRowIndex;
|
||||
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByColumnIndex;
|
||||
|
||||
// We only display annotations that span a single row.
|
||||
if (this->settings.displayAnnotations && firstByteRowIndex == lastByteRowIndex) {
|
||||
annotationItem->show();
|
||||
auto rowIndicesWithTopAnnotations = std::set<std::size_t>();
|
||||
auto rowIndicesWithBottomAnnotations = std::set<std::size_t>();
|
||||
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
|
||||
|
||||
for (auto* annotationItem : this->annotationItems) {
|
||||
const auto firstByteRowIndex = static_cast<std::size_t>(
|
||||
std::ceil(static_cast<double>((annotationItem->startAddress - memoryAddressRange.startAddress) + 1)
|
||||
/ static_cast<double>(columnCount)) - 1
|
||||
);
|
||||
|
||||
const auto lastByteRowIndex = static_cast<std::size_t>(
|
||||
std::ceil(static_cast<double>((annotationItem->endAddress - memoryAddressRange.startAddress) + 1)
|
||||
/ static_cast<double>(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::size_t>(
|
||||
std::ceil(static_cast<double>(byteWidget->byteIndex + 1) / static_cast<double>(columnCount)) - 1
|
||||
);
|
||||
const auto columnIndex = static_cast<std::size_t>(
|
||||
static_cast<double>(byteWidget->byteIndex)
|
||||
- (std::floor(byteWidget->byteIndex / columnCount) * static_cast<double>(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<int>(
|
||||
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::size_t>(
|
||||
std::ceil(static_cast<double>(byteWidget->byteIndex + 1) / static_cast<double>(columnCount)) - 1
|
||||
);
|
||||
const auto columnIndex = static_cast<std::size_t>(
|
||||
static_cast<double>(byteWidget->byteIndex)
|
||||
- (std::floor(byteWidget->byteIndex / columnCount) * static_cast<double>(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<int>(
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FocusedMemoryRegion>& focusedMemoryRegions,
|
||||
std::vector<ExcludedMemoryRegion>& 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<FocusedMemoryRegion>& focusedMemoryRegions,
|
||||
std::vector<ExcludedMemoryRegion>& 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<QWidget*>("tool-bar");
|
||||
this->bottomBar = this->container->findChild<QWidget*>("bottom-bar");
|
||||
if (!widgetUiFile.open(QFile::ReadOnly)) {
|
||||
throw Exception("Failed to open HexViewerWidget UI file");
|
||||
}
|
||||
|
||||
this->refreshButton = this->toolBar->findChild<SvgToolButton*>("refresh-memory-btn");
|
||||
this->highlightStackMemoryButton = this->toolBar->findChild<SvgToolButton*>("highlight-stack-memory-btn");
|
||||
this->highlightHoveredRowAndColumnButton = this->toolBar->findChild<SvgToolButton*>(
|
||||
"highlight-hovered-rows-columns-btn"
|
||||
);
|
||||
this->highlightFocusedMemoryButton = this->container->findChild<SvgToolButton*>(
|
||||
"highlight-focused-memory-btn"
|
||||
);
|
||||
this->displayAnnotationsButton = this->container->findChild<SvgToolButton*>("display-annotations-btn");
|
||||
this->displayAsciiButton = this->container->findChild<SvgToolButton*>("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<TextInput*>("go-to-address-input");
|
||||
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
|
||||
this->bottomBar = this->container->findChild<QWidget*>("bottom-bar");
|
||||
|
||||
this->toolBar->setContentsMargins(0, 0, 0, 0);
|
||||
this->toolBar->layout()->setContentsMargins(5, 0, 5, 1);
|
||||
this->refreshButton = this->toolBar->findChild<SvgToolButton*>("refresh-memory-btn");
|
||||
this->highlightStackMemoryButton = this->toolBar->findChild<SvgToolButton*>(
|
||||
"highlight-stack-memory-btn"
|
||||
);
|
||||
this->highlightHoveredRowAndColumnButton = this->toolBar->findChild<SvgToolButton*>(
|
||||
"highlight-hovered-rows-columns-btn"
|
||||
);
|
||||
this->highlightFocusedMemoryButton = this->container->findChild<SvgToolButton*>(
|
||||
"highlight-focused-memory-btn"
|
||||
);
|
||||
this->displayAnnotationsButton = this->container->findChild<SvgToolButton*>("display-annotations-btn");
|
||||
this->displayAsciiButton = this->container->findChild<SvgToolButton*>("display-ascii-btn");
|
||||
|
||||
this->bottomBar->setContentsMargins(0, 0, 0, 0);
|
||||
this->bottomBar->layout()->setContentsMargins(5, 0, 5, 0);
|
||||
this->goToAddressInput = this->container->findChild<TextInput*>("go-to-address-input");
|
||||
|
||||
this->hoveredAddressLabel = this->bottomBar->findChild<QLabel*>("byte-address-label");
|
||||
this->toolBar->setContentsMargins(0, 0, 0, 0);
|
||||
this->toolBar->layout()->setContentsMargins(5, 0, 5, 1);
|
||||
|
||||
this->byteItemGraphicsViewContainer = this->container->findChild<QWidget*>("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<QLabel*>("byte-address-label");
|
||||
|
||||
if (this->targetMemoryDescriptor.type == Targets::TargetMemoryType::RAM) {
|
||||
this->highlightStackMemoryButton->show();
|
||||
this->setStackMemoryHighlightingEnabled(this->settings.highlightStackMemory);
|
||||
this->byteItemGraphicsViewContainer = this->container->findChild<QWidget*>("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({});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,68 +2,46 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
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<int8_t>(this->value[0]));
|
||||
break;
|
||||
}
|
||||
this->refreshLabelText();
|
||||
this->setToolTip(this->labelText);
|
||||
}
|
||||
|
||||
if (valueSize == 2) {
|
||||
this->labelText = QString::number(static_cast<int16_t>((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<int8_t>(this->value[0]));
|
||||
break;
|
||||
}
|
||||
|
||||
if (valueSize == 2) {
|
||||
this->labelText = QString::number(static_cast<int16_t>((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<const char*>(asciiData.data()),
|
||||
static_cast<qsizetype>(asciiData.size())
|
||||
) + "'";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
|
||||
this->labelText = "'" + QString::fromLatin1(
|
||||
reinterpret_cast<const char*>(asciiData.data()),
|
||||
static_cast<qsizetype>(asciiData.size())
|
||||
) + "'";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<QComboBox*>("data-type-input");
|
||||
this->endiannessInput = this->formWidget->findChild<QComboBox*>("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<QComboBox*>("data-type-input");
|
||||
this->endiannessInput = this->formWidget->findChild<QComboBox*>("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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FocusedMemoryRegion>& focusedMemoryRegions,
|
||||
std::vector<ExcludedMemoryRegion>& 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<QWidget*>("region-selector");
|
||||
auto* regionSelectorToolBar = this->regionSelector->findChild<QWidget*>("region-selector-tool-bar");
|
||||
this->addRegionButton = this->regionSelector->findChild<SvgToolButton*>("add-region-btn");
|
||||
this->removeRegionButton = this->regionSelector->findChild<SvgToolButton*>("remove-region-btn");
|
||||
this->addFocusedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-focused-region");
|
||||
this->addExcludedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-excluded-region");
|
||||
this->regionItemScrollArea = this->regionSelector->findChild<QScrollArea*>("region-item-scroll-area");
|
||||
this->regionItemScrollAreaViewport = this->regionItemScrollArea->findChild<QWidget*>("item-container");
|
||||
this->regionItemScrollAreaViewportLayout = this->regionItemScrollAreaViewport->findChild<QVBoxLayout*>(
|
||||
"item-container-layout"
|
||||
);
|
||||
|
||||
this->stackedFormLayout = this->container->findChild<QStackedLayout*>("stacked-form-layout");
|
||||
|
||||
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
|
||||
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
|
||||
this->closeButton = this->container->findChild<QPushButton*>("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<RegionItem*>(
|
||||
this->regionItemScrollAreaViewportLayout->itemAt(0)->widget()
|
||||
MemoryRegionManagerWindow::MemoryRegionManagerWindow(
|
||||
const Targets::TargetMemoryDescriptor& memoryDescriptor,
|
||||
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
|
||||
std::vector<ExcludedMemoryRegion>& 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<RegionItem*, decltype(regionItemCompare)>(regionItemCompare);
|
||||
|
||||
QLayoutItem* layoutItem = nullptr;
|
||||
while ((layoutItem = this->regionItemScrollAreaViewportLayout->takeAt(0)) != nullptr) {
|
||||
auto* regionItem = qobject_cast<RegionItem*>(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<FocusedRegionItem*>(regionItem);
|
||||
|
||||
if (focusedRegionItem != nullptr) {
|
||||
this->focusedRegionItems.erase(focusedRegionItem);
|
||||
}
|
||||
|
||||
} else {
|
||||
auto* excludedRegionItem = qobject_cast<ExcludedRegionItem*>(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<RegionItem*>(
|
||||
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<QWidget*>("region-selector");
|
||||
auto* regionSelectorToolBar = this->regionSelector->findChild<QWidget*>("region-selector-tool-bar");
|
||||
this->addRegionButton = this->regionSelector->findChild<SvgToolButton*>("add-region-btn");
|
||||
this->removeRegionButton = this->regionSelector->findChild<SvgToolButton*>("remove-region-btn");
|
||||
this->addFocusedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-focused-region");
|
||||
this->addExcludedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-excluded-region");
|
||||
this->regionItemScrollArea = this->regionSelector->findChild<QScrollArea*>("region-item-scroll-area");
|
||||
this->regionItemScrollAreaViewport = this->regionItemScrollArea->findChild<QWidget*>("item-container");
|
||||
this->regionItemScrollAreaViewportLayout = this->regionItemScrollAreaViewport->findChild<QVBoxLayout*>(
|
||||
"item-container-layout"
|
||||
);
|
||||
|
||||
this->stackedFormLayout = this->container->findChild<QStackedLayout*>("stacked-form-layout");
|
||||
|
||||
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
|
||||
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
|
||||
this->closeButton = this->container->findChild<QPushButton*>("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<FocusedMemoryRegion>();
|
||||
auto processedExcludedMemoryRegions = std::vector<ExcludedMemoryRegion>();
|
||||
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() + "\""
|
||||
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
|
||||
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<RegionItem*>(
|
||||
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<RegionItem*, decltype(regionItemCompare)>(regionItemCompare);
|
||||
|
||||
QLayoutItem* layoutItem = nullptr;
|
||||
while ((layoutItem = this->regionItemScrollAreaViewportLayout->takeAt(0)) != nullptr) {
|
||||
auto* regionItem = qobject_cast<RegionItem*>(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<FocusedRegionItem*>(regionItem);
|
||||
|
||||
if (focusedRegionItem != nullptr) {
|
||||
this->focusedRegionItems.erase(focusedRegionItem);
|
||||
}
|
||||
|
||||
} else {
|
||||
auto* excludedRegionItem = qobject_cast<ExcludedRegionItem*>(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() + "\""
|
||||
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
|
||||
this
|
||||
if (this->regionItemScrollAreaViewportLayout->count() > 0) {
|
||||
auto* regionItem = qobject_cast<RegionItem*>(
|
||||
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<FocusedMemoryRegion>();
|
||||
auto processedExcludedMemoryRegions = std::vector<ExcludedMemoryRegion>();
|
||||
|
||||
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() + "\""
|
||||
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
|
||||
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() + "\""
|
||||
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
|
||||
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"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,217 +3,228 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QString>
|
||||
|
||||
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<TextInput*>("name-input");
|
||||
this->addressTypeInput = this->formWidget->findChild<QComboBox*>("address-type-input");
|
||||
this->startAddressInput = this->formWidget->findChild<TextInput*>("start-address-input");
|
||||
this->endAddressInput = this->formWidget->findChild<TextInput*>("end-address-input");
|
||||
this->sizeInput = this->formWidget->findChild<TextInput*>("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<TextInput*>("name-input");
|
||||
this->addressTypeInput = this->formWidget->findChild<QComboBox*>("address-type-input");
|
||||
this->startAddressInput = this->formWidget->findChild<TextInput*>("start-address-input");
|
||||
this->endAddressInput = this->formWidget->findChild<TextInput*>("end-address-input");
|
||||
this->sizeInput = this->formWidget->findChild<TextInput*>("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);
|
||||
}
|
||||
|
||||
@@ -4,75 +4,79 @@
|
||||
#include <QScrollArea>
|
||||
#include <QMargins>
|
||||
|
||||
using namespace Bloom::Widgets;
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
BitBodyWidget::BitBodyWidget(
|
||||
int bitIndex,
|
||||
std::bitset<std::numeric_limits<unsigned char>::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<std::numeric_limits<unsigned char>::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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,50 +6,51 @@
|
||||
|
||||
#include "BitBodyWidget.hpp"
|
||||
|
||||
using namespace Bloom::Widgets;
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
BitWidget::BitWidget(
|
||||
int bitIndex,
|
||||
int bitNumber,
|
||||
std::bitset<std::numeric_limits<unsigned char>::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<std::numeric_limits<unsigned char>::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<size_t>(this->bitIndex)],
|
||||
this->readOnly,
|
||||
this
|
||||
);
|
||||
|
||||
this->body = new BitBodyWidget(
|
||||
this->bitIndex,
|
||||
this->bitset[static_cast<size_t>(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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<int>((BitWidget::WIDTH + BitWidget::SPACING) * this->bitset.size() - BitWidget::SPACING),
|
||||
BitsetWidget::HEIGHT
|
||||
);
|
||||
|
||||
for (int bitIndex = (std::numeric_limits<unsigned char>::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<int>((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<unsigned char>(this->bitset.to_ulong());
|
||||
this->repaint();
|
||||
emit this->byteChanged();
|
||||
}
|
||||
for (int bitIndex = (std::numeric_limits<unsigned char>::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<unsigned char>(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<int>(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<int>((BitWidget::WIDTH / 2) + width),
|
||||
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
|
||||
));
|
||||
|
||||
painter.drawLine(QLine(
|
||||
static_cast<int>((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<int>((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<int>(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<int>((BitWidget::WIDTH / 2) + width),
|
||||
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
|
||||
));
|
||||
|
||||
painter.drawLine(QLine(
|
||||
static_cast<int>((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<int>((BitWidget::WIDTH / 2) + width),
|
||||
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - (labelHeight / 2),
|
||||
labelWidth,
|
||||
labelHeight
|
||||
),
|
||||
Qt::AlignCenter,
|
||||
byteHex
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
#include <QStyle>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,27 @@
|
||||
|
||||
#include <QStyle>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,38 +4,39 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QByteArray>
|
||||
|
||||
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<const char*>(registerValue.data()),
|
||||
static_cast<qsizetype>(registerValue.size())
|
||||
).toHex()).toUpper());
|
||||
this->valueLabel->setObjectName("value-label");
|
||||
|
||||
this->valueLabel->setText("0x" + QString(QByteArray(
|
||||
reinterpret_cast<const char*>(registerValue.data()),
|
||||
static_cast<qsizetype>(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<QWidget*>("item-container");
|
||||
this->itemContainerLayout = this->itemContainer->findChild<QVBoxLayout*>("item-container-layout");
|
||||
auto titleBar = this->container->findChild<QWidget*>("title-bar");
|
||||
auto title = titleBar->findChild<QLabel*>("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<QWidget*>("item-container");
|
||||
this->itemContainerLayout = this->itemContainer->findChild<QVBoxLayout*>("item-container-layout");
|
||||
auto titleBar = this->container->findChild<QWidget*>("title-bar");
|
||||
auto title = titleBar->findChild<QLabel*>("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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Targets::TargetMemoryBuffer> 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<Targets::TargetMemoryBuffer>& 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<int>(
|
||||
440
|
||||
+ ((BitsetWidget::HEIGHT + 20) * std::ceil(static_cast<float>(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<QLabel*>("register-name");
|
||||
this->registerDescriptionLabel = this->container->findChild<QLabel*>("register-description");
|
||||
this->contentContainer = this->container->findChild<QWidget*>("content-container");
|
||||
this->registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
|
||||
this->registerValueTextInput = this->container->findChild<QLineEdit*>("register-value-text-input");
|
||||
this->registerValueBitsetWidgetContainer = this->container->findChild<QWidget*>("register-value-bitset-widget-container");
|
||||
this->refreshValueButton = this->container->findChild<QPushButton*>("refresh-value-btn");
|
||||
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
|
||||
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
|
||||
this->closeButton = this->container->findChild<QPushButton*>("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<QHBoxLayout*>("content-layout");
|
||||
contentLayout->insertWidget(0, this->registerHistoryWidget, 0, Qt::AlignmentFlag::AlignTop);
|
||||
|
||||
auto* registerDetailsContainer = this->container->findChild<QWidget*>("register-details-container");
|
||||
auto* registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
|
||||
registerValueContainer->setContentsMargins(15, 15, 15, 15);
|
||||
registerDetailsContainer->setContentsMargins(15, 15, 15, 15);
|
||||
|
||||
auto* registerDetailsNameInput = registerDetailsContainer->findChild<QLineEdit*>("register-details-name-input");
|
||||
auto* registerDetailsSizeInput = registerDetailsContainer->findChild<QLineEdit*>("register-details-size-input");
|
||||
auto* registerDetailsStartAddressInput = registerDetailsContainer->findChild<QLineEdit*>(
|
||||
"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<QLabel*>("read-only-indicator-label");
|
||||
readOnlyIndicatorLabel->show();
|
||||
}
|
||||
|
||||
auto* registerBitsetWidgetLayout = this->registerValueBitsetWidgetContainer->findChild<QVBoxLayout*>(
|
||||
"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<int>(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<int>(
|
||||
440
|
||||
+ ((BitsetWidget::HEIGHT + 20) * std::ceil(static_cast<float>(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<QLabel*>("register-name");
|
||||
this->registerDescriptionLabel = this->container->findChild<QLabel*>("register-description");
|
||||
this->contentContainer = this->container->findChild<QWidget*>("content-container");
|
||||
this->registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
|
||||
this->registerValueTextInput = this->container->findChild<QLineEdit*>("register-value-text-input");
|
||||
this->registerValueBitsetWidgetContainer = this->container->findChild<QWidget*>(
|
||||
"register-value-bitset-widget-container"
|
||||
);
|
||||
this->refreshValueButton = this->container->findChild<QPushButton*>("refresh-value-btn");
|
||||
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
|
||||
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
|
||||
this->closeButton = this->container->findChild<QPushButton*>("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<QHBoxLayout*>("content-layout");
|
||||
contentLayout->insertWidget(0, this->registerHistoryWidget, 0, Qt::AlignmentFlag::AlignTop);
|
||||
|
||||
auto* registerDetailsContainer = this->container->findChild<QWidget*>("register-details-container");
|
||||
auto* registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
|
||||
registerValueContainer->setContentsMargins(15, 15, 15, 15);
|
||||
registerDetailsContainer->setContentsMargins(15, 15, 15, 15);
|
||||
|
||||
auto* registerDetailsNameInput = registerDetailsContainer->findChild<QLineEdit*>(
|
||||
"register-details-name-input"
|
||||
);
|
||||
auto* registerDetailsSizeInput = registerDetailsContainer->findChild<QLineEdit*>(
|
||||
"register-details-size-input"
|
||||
);
|
||||
auto* registerDetailsStartAddressInput = registerDetailsContainer->findChild<QLineEdit*>(
|
||||
"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<QLabel*>(
|
||||
"read-only-indicator-label"
|
||||
);
|
||||
readOnlyIndicatorLabel->show();
|
||||
}
|
||||
|
||||
auto* registerBitsetWidgetLayout = this->registerValueBitsetWidgetContainer->findChild<QVBoxLayout*>(
|
||||
"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<int>(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<unsigned char>(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<unsigned char>(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<const char*>(this->registerValue.data()),
|
||||
static_cast<qsizetype>(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<const char*>(this->registerValue.data()),
|
||||
static_cast<qsizetype>(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"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Bloom::Widgets
|
||||
const Targets::TargetRegisterDescriptor& registerDescriptor,
|
||||
InsightWorker& insightWorker,
|
||||
Targets::TargetState currentTargetState,
|
||||
std::optional<Targets::TargetMemoryBuffer> registerValue = std::nullopt,
|
||||
const std::optional<Targets::TargetMemoryBuffer>& registerValue = std::nullopt,
|
||||
QWidget* parent = nullptr
|
||||
);
|
||||
|
||||
|
||||
@@ -2,27 +2,28 @@
|
||||
|
||||
#include <QStyle>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<TargetRegisterDescriptor>& registerDescriptors,
|
||||
InsightWorker& insightWorker,
|
||||
TargetRegistersPaneWidget* parent
|
||||
): ItemWidget(parent), name(std::move(name)) {
|
||||
this->setObjectName(this->name);
|
||||
RegisterGroupWidget::RegisterGroupWidget(
|
||||
QString name,
|
||||
const std::set<TargetRegisterDescriptor>& 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<const char*>(targetRegister.value.data()),
|
||||
static_cast<qsizetype>(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<const char*>(targetRegister.value.data()),
|
||||
static_cast<qsizetype>(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<const char*>(this->currentRegister.value().value.data()),
|
||||
static_cast<qsizetype>(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<const char*>(this->currentRegister.value().value.data()),
|
||||
static_cast<qsizetype>(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<qsizetype>(this->currentRegister.value().size());
|
||||
auto valueByteArray = QByteArray(
|
||||
reinterpret_cast<const char*>(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<const char*>(this->currentRegister.value().value.data()),
|
||||
static_cast<qsizetype>(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<const char*>(this->currentRegister.value().value.data()),
|
||||
static_cast<qsizetype>(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<qsizetype>(this->currentRegister.value().size());
|
||||
auto valueByteArray = QByteArray(
|
||||
reinterpret_cast<const char*>(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<QWidget*>("tool-bar");
|
||||
this->collapseAllButton = this->toolBar->findChild<SvgToolButton*>("collapse-all-btn");
|
||||
this->expandAllButton = this->toolBar->findChild<SvgToolButton*>("expand-all-btn");
|
||||
this->toolBar->layout()->setContentsMargins(5, 0, 5, 0);
|
||||
this->searchInput = this->container->findChild<QLineEdit*>("search-input");
|
||||
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
|
||||
this->collapseAllButton = this->toolBar->findChild<SvgToolButton*>("collapse-all-btn");
|
||||
this->expandAllButton = this->toolBar->findChild<SvgToolButton*>("expand-all-btn");
|
||||
this->toolBar->layout()->setContentsMargins(5, 0, 5, 0);
|
||||
this->searchInput = this->container->findChild<QLineEdit*>("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<QScrollArea*>("item-scroll-area");
|
||||
this->itemScrollArea = this->container->findChild<QScrollArea*>("item-scroll-area");
|
||||
|
||||
this->itemContainer = this->container->findChild<QWidget*>("item-container");
|
||||
auto itemLayout = this->itemContainer->findChild<QVBoxLayout*>();
|
||||
this->itemContainer = this->container->findChild<QWidget*>("item-container");
|
||||
auto itemLayout = this->itemContainer->findChild<QVBoxLayout*>();
|
||||
|
||||
auto& registerDescriptors = targetDescriptor.registerDescriptorsByType;
|
||||
this->renderedDescriptors = std::set<TargetRegisterDescriptor>(
|
||||
registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).begin(),
|
||||
registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).end()
|
||||
);
|
||||
auto& registerDescriptors = targetDescriptor.registerDescriptorsByType;
|
||||
this->renderedDescriptors = std::set<TargetRegisterDescriptor>(
|
||||
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<std::string, std::set<TargetRegisterDescriptor>>();
|
||||
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<std::string, std::set<TargetRegisterDescriptor>>();
|
||||
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<std::function<void(void)>> 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<std::function<void(void)>> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<int>(std::ceil(3.6 * static_cast<double>(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<int>(std::ceil(3.6 * static_cast<double>(pinCount)))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
);
|
||||
|
||||
this->firstPinIndicatorDiameter = std::min(
|
||||
BodyWidget::MAXIMUM_FIRST_PIN_INDICATOR_HEIGHT,
|
||||
std::max(BodyWidget::MINIMUM_FIRST_PIN_INDICATOR_HEIGHT, static_cast<int>(std::floor(pinCount / 2)))
|
||||
);
|
||||
this->firstPinIndicatorDiameter = std::min(
|
||||
BodyWidget::MAXIMUM_FIRST_PIN_INDICATOR_HEIGHT,
|
||||
std::max(BodyWidget::MINIMUM_FIRST_PIN_INDICATOR_HEIGHT, static_cast<int>(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<int>(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<int>(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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,34 +2,39 @@
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -2,47 +2,51 @@
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,39 +3,44 @@
|
||||
#include <QPainter>
|
||||
#include <QEvent>
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<int>(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<int>(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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<std::function<void(void)>> 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<std::function<void(void)>> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user