Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions SerialPrograms/Source/CommonFramework/ProgramSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ ProgramSession::ProgramSession(const ProgramDescriptor& descriptor)
, m_logger(global_logger_raw(), "Program")
, m_timestamp(current_time())
, m_state(ProgramState::STOPPED)
, m_download_manager(descriptor.required_resources())
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has me thinking. What if there are multiple programs at the same time and thus multiple DownloadManagers? Will they conflict? Is it possible for two instances to simultaneously try to download the same resource at the same time?

{
load_historical_stats();
}
Expand Down
4 changes: 4 additions & 0 deletions SerialPrograms/Source/CommonFramework/ProgramSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "Common/Cpp/ListenerSet.h"
#include "Common/Cpp/Concurrency/Mutex.h"
#include "Common/Cpp/Concurrency/AsyncTask.h"
#include "CommonFramework/ResourceDownload/RequiredDownloadManager.h"
#include "CommonFramework/Globals.h"
//#include "CommonFramework/Logging/Logger.h"
#include "Integrations/ProgramTrackerInterfaces.h"
Expand Down Expand Up @@ -59,6 +60,7 @@ class ProgramSession : public TrackableProgram{
const ProgramDescriptor& descriptor() const{ return m_descriptor; }
uint64_t instance_id() const{ return m_instance_id; }
Logger& logger(){ return m_logger; }
RequiredDownloadManager& get_download_manager(){ return m_download_manager; }


public:
Expand Down Expand Up @@ -140,6 +142,8 @@ class ProgramSession : public TrackableProgram{
// CancellableScope* m_scope = nullptr;

ListenerSet<Listener> m_listeners;

RequiredDownloadManager m_download_manager;
};


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
/* Required Download Dialog Widget
*
* From: https://github.com/PokemonAutomation/
*
*/

#include <QVBoxLayout>
#include <QMessageBox>
// #include <QFont>
// #include <QObject>
// #include <QPushButton>
// #include <QPointer>
// #include <QHBoxLayout>
// #include "CommonFramework/Logging/Logger.h"
#include "Common/Cpp/Exceptions.h"

// #include "CommonFramework/Notifications/ProgramNotifications.h"
#include "RequiredDownloadDialogWidget.h"

#include <iostream>
using std::cout;
using std::endl;

namespace PokemonAutomation{


////////////////////////////////////////////
// RequiredDownloadWidget
////////////////////////////////////////////

RequiredDownloadWidget::~RequiredDownloadWidget(){
m_value->remove_listener(*this);
}

RequiredDownloadWidget::RequiredDownloadWidget(QWidget& parent, std::shared_ptr<RequiredDownload> download_ptr)
: QWidget(&parent)
, m_value(download_ptr)
{

QHBoxLayout* mainLayout = new QHBoxLayout(this);
// Create a label for the specific task
m_resource_name = new QLabel(QString::fromStdString(download_ptr->get_name()), this);
m_resource_name->setFixedWidth(300);
mainLayout->addWidget(m_resource_name);

// Create a label for the status
m_status_label = new QLabel("", this);
m_status_label->setFixedWidth(70);
mainLayout->addWidget(m_status_label);

// Create the progress bar
m_progress_bar = new QProgressBar(this);
m_progress_bar->setRange(0, 100);
m_progress_bar->setValue(0);
m_progress_bar->setFixedWidth(100);
mainLayout->addWidget(m_progress_bar);

download_ptr->add_listener(*this);
}


void RequiredDownloadWidget::update_progress_bar(uint64_t bytes_done, uint64_t total_bytes, const std::string& text){
double percent = total_bytes > 0 ? (static_cast<double>(bytes_done) / total_bytes) * 100.0 : 0;
int current_percent = static_cast<int>(percent);
int last_percentage = m_progress_bar->value();
// Only update UI if integer value has changed
if (current_percent == last_percentage){
return;
}

// current_percent has changed. update the progress bar
m_status_label->setText(QString::fromStdString(text));
m_progress_bar->setValue(current_percent);
}

void RequiredDownloadWidget::on_download_progress(uint64_t bytes_done, uint64_t total_bytes){
QMetaObject::invokeMethod(this, [this, bytes_done, total_bytes]{
update_progress_bar(bytes_done, total_bytes, "Downloading");
}, Qt::QueuedConnection);
}
void RequiredDownloadWidget::on_unzip_progress(uint64_t bytes_done, uint64_t total_bytes){
QMetaObject::invokeMethod(this, [this, bytes_done, total_bytes]{
update_progress_bar(bytes_done, total_bytes, "Unzipping");
}, Qt::QueuedConnection);
}
void RequiredDownloadWidget::on_hash_progress(uint64_t bytes_done, uint64_t total_bytes){
QMetaObject::invokeMethod(this, [this, bytes_done, total_bytes]{
update_progress_bar(bytes_done, total_bytes, "Verifying");
}, Qt::QueuedConnection);
}

void RequiredDownloadWidget::on_download_failed(){
std::cerr << "Error: Download failed. Check your internet connection and check you have enough disk space." << std::endl;
QMetaObject::invokeMethod(this, []{
QMessageBox box;
box.critical(nullptr, "Error",
QString::fromStdString("Error: Download failed. Check your internet connection and check you have enough disk space."));
});
}

////////////////////////////////////////////
// RequiredDownloadDialogWidget
////////////////////////////////////////////

RequiredDownloadDialogWidget::~RequiredDownloadDialogWidget(){
m_download_manager.remove_download_listener(*this);
}
RequiredDownloadDialogWidget::RequiredDownloadDialogWidget(QWidget& parent, RequiredDownloadManager& download_manager)
: QDialog (&parent)
, m_download_manager(download_manager)
{
setWindowTitle("Task Progress");
resize(400, 50 * (int)download_manager.get_required_downloads().size() + 50); // Dynamically scale height

QVBoxLayout* mainLayout = new QVBoxLayout(this);

for (std::shared_ptr<RequiredDownload> download_ptr : download_manager.get_required_downloads()){
// cout << download_ptr->get_name() << endl;

RequiredDownloadWidget* download_layout = new RequiredDownloadWidget(*this, download_ptr);
download_ptr->start_download();

mainLayout->addWidget(download_layout);

// Add a small spacing gap between tasks
mainLayout->addSpacing(0);
}

// 2. Create standard Dialog Buttons (OK and Cancel)
QDialogButtonBox* buttonBox = new QDialogButtonBox(
QDialogButtonBox::Cancel,
this
);

// 3. Connect the buttons to the default QDialog slots
// Clicking OK calls accept(), clicking Cancel calls reject()
// connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);

// 4. Add the buttons to the bottom of the layout
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);

download_manager.add_download_listener(*this);
}


void RequiredDownloadDialogWidget::on_all_downloads_finished(){
cout << "All downloads finished. Start the program." << endl;
QMetaObject::invokeMethod(this, [this](){
this->accept();
});
}
void RequiredDownloadDialogWidget::on_download_failed(){
std::cerr << "Error: Download failed. Check your internet connection and check you have enough disk space." << std::endl;
QMetaObject::invokeMethod(this, [this]{
this->reject();
QMessageBox box;
box.critical(nullptr, "Error",
QString::fromStdString("Error: Download failed. Check your internet connection and check you have enough disk space."));
});

}
void RequiredDownloadDialogWidget::on_exception_caught(const std::string& function_name){
std::cerr << "Error: Exception thrown in thread. From " + function_name + ". Report this as a bug." << std::endl;
QMetaObject::invokeMethod(this, [this, function_name]{
this->reject();

QMessageBox box;
box.critical(nullptr, "Error",
QString::fromStdString("Error: Exception thrown in thread. From " + function_name + ". Report this as a bug."));
});
}

/////////////////////////
// Non member functions
/////////////////////////


bool show_download_prereqs_popup(
QWidget* parent,
RequiredDownloadManager& download_manager,
std::function<void(const std::string&)> error_callback
){

try{

// RequiredDownloadManager& download_manager = m_session.get_download_manager();
// re-initialize the required downloads, even if already initialized
// this refreshes the download list, which may have changed since the last run
download_manager.initialize_required_downloads();

if (download_manager.get_upgrade_warning()){
std::string warning_string =
"The program is expecting an older version of a resource than is available. "
"This likely means that your version of Computer Control is out of date. "
"We recommend that you upgrade the Computer Control program.";
error_callback(warning_string);
// cout << warning_string << endl;
}
if (download_manager.get_required_downloads().empty()){
cout << "required_download_list is empty. Start the program." << endl;

return true;
}

RequiredDownloadDialogWidget box{*parent, download_manager};

// box.open();
if (box.exec() == QDialog::Accepted){
cout << "Pre-req downloads done" << endl;
return true;
}else{
cout << "Pre-req downloads NOT done." << endl;
download_manager.cancel_downloads();
return false;
}

}catch(InternalProgramError& e){
error_callback(e.message());
}catch (const std::exception& e) {
std::string message = std::string(e.what()) + "Report this as an error.";
error_callback(message);
}catch(...){
error_callback("show_download_prereqs_popup: Unknown exception caught. Report this as an error.");
}

return false;

}




}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* Required Download Dialog Widget
*
* From: https://github.com/PokemonAutomation/
*
*/

#ifndef PokemonAutomation_RequiredDownloadDialogWidget_H
#define PokemonAutomation_RequiredDownloadDialogWidget_H

#include <QDialog>
#include <QLabel>
#include <QProgressBar>
#include "RequiredDownloadManager.h"
#include "CommonFramework/ResourceDownload/RequiredDownload.h"


namespace PokemonAutomation{


class RequiredDownloadWidget : public QWidget, public RequiredDownload::Listener {
public:
~RequiredDownloadWidget();
RequiredDownloadWidget(QWidget& parent, std::shared_ptr<RequiredDownload> download_ptr);

public:
virtual void on_download_progress(uint64_t bytes_done, uint64_t total_bytes) override;
virtual void on_unzip_progress(uint64_t bytes_done, uint64_t total_bytes) override;
virtual void on_hash_progress(uint64_t bytes_done, uint64_t total_bytes) override;

virtual void on_download_failed() override;


void update_progress_bar(uint64_t bytes_done, uint64_t total_bytes, const std::string& text);

private:
std::shared_ptr<RequiredDownload> m_value;
QLabel* m_resource_name;
QLabel* m_status_label;
QProgressBar* m_progress_bar;

};

class RequiredDownloadDialogWidget : public QDialog, public RequiredDownloadManager::DownloadListener {
public:

public:
~RequiredDownloadDialogWidget();
RequiredDownloadDialogWidget(QWidget& parent, RequiredDownloadManager& download_manager);

public:
virtual void on_all_downloads_finished() override;
virtual void on_download_failed() override;
virtual void on_exception_caught(const std::string& function_name) override;

// virtual void on_action_state_updated() override;

private:
// void update_UI_state();
// void update_progress_bar(int percentage, const std::string& text);
// void update_progress_bar(uint64_t bytes_done, uint64_t total_bytes, const std::string& text);
private:
RequiredDownloadManager& m_download_manager;

};

// Opens a pop-up box that downloads all the pre-requisite resources
// that haven't yet been downloaded.
// return true if all pre-requisite resources are downloaded
bool show_download_prereqs_popup(
QWidget* parent,
RequiredDownloadManager& download_manager,
std::function<void(const std::string&)> error_callback
);




}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@
#include "CommonFramework/Panels/PanelTools.h"
#include "CommonFramework/Panels/UI/PanelElements.h"
#include "CommonFramework/ProgramStats/StatsTracking.h"
#include "CommonFramework/ResourceDownload/RequiredDownloadDialogWidget.h"
#include "ComputerPrograms/ComputerProgram.h"
#include "ComputerPrograms/Framework/ComputerProgramOption.h"
#include "ComputerProgramWidget.h"

// #include <iostream>
// using std::cout;
// using std::endl;

namespace PokemonAutomation{


Expand Down Expand Up @@ -77,9 +82,18 @@ ComputerProgramWidget::ComputerProgramWidget(
this, [&](ProgramState state){
std::string error;
switch (state){
case ProgramState::STOPPED:
error = m_session.start_program();
case ProgramState::STOPPED:{
bool prereqs_downloaded = show_download_prereqs_popup(this, m_session.get_download_manager(),
[this](const std::string& msg) {
this->error(msg);
}
);

if (prereqs_downloaded){
error = m_session.start_program();
}
break;
}
case ProgramState::RUNNING:
error = m_session.stop_program();
break;
Expand Down
Loading
Loading