From: Joel Holdsworth Date: Thu, 9 Jan 2014 18:58:31 +0000 (+0000) Subject: Added support for save X-Git-Tag: pulseview-0.2.0~156 X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=commitdiff_plain;h=0fbda3c2dda9357776afa15e99c037eb0cc97214;hp=02412f0b16cb5ad92654c92b472a84268e79bb54 Added support for save --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b8f9fa8..94d32a41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,6 +107,7 @@ set(pulseview_SOURCES pv/devicemanager.cpp pv/mainwindow.cpp pv/sigsession.cpp + pv/storesession.cpp pv/data/analog.cpp pv/data/analogsnapshot.cpp pv/data/logic.cpp @@ -115,6 +116,7 @@ set(pulseview_SOURCES pv/data/snapshot.cpp pv/dialogs/about.cpp pv/dialogs/connect.cpp + pv/dialogs/storeprogress.cpp pv/popups/deviceoptions.cpp pv/popups/probes.cpp pv/prop/bool.cpp @@ -151,8 +153,10 @@ set(pulseview_SOURCES set(pulseview_HEADERS pv/mainwindow.h pv/sigsession.h + pv/storesession.h pv/dialogs/about.h pv/dialogs/connect.h + pv/dialogs/storeprogress.h pv/popups/probes.h pv/popups/deviceoptions.h pv/prop/bool.h diff --git a/icons/document-save-as.png b/icons/document-save-as.png new file mode 100644 index 00000000..0f7b2994 Binary files /dev/null and b/icons/document-save-as.png differ diff --git a/pulseview.qrc b/pulseview.qrc index d2e1c139..a18d6559 100644 --- a/pulseview.qrc +++ b/pulseview.qrc @@ -4,6 +4,7 @@ icons/configure.png icons/decoder-delete.svg icons/document-open.png + icons/document-save-as.png icons/probes.svg icons/sigrok-logo-notext.png icons/status-green.svg diff --git a/pv/data/logicsnapshot.cpp b/pv/data/logicsnapshot.cpp index f2fa81bc..177c6627 100644 --- a/pv/data/logicsnapshot.cpp +++ b/pv/data/logicsnapshot.cpp @@ -78,9 +78,9 @@ void LogicSnapshot::get_samples(uint8_t *const data, { assert(data); assert(start_sample >= 0); - assert(start_sample < (int64_t)_sample_count); + assert(start_sample <= (int64_t)_sample_count); assert(end_sample >= 0); - assert(end_sample < (int64_t)_sample_count); + assert(end_sample <= (int64_t)_sample_count); assert(start_sample <= end_sample); lock_guard lock(_mutex); diff --git a/pv/dialogs/storeprogress.cpp b/pv/dialogs/storeprogress.cpp new file mode 100644 index 00000000..e0f670d3 --- /dev/null +++ b/pv/dialogs/storeprogress.cpp @@ -0,0 +1,82 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2014 Joel Holdsworth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "storeprogress.h" + +namespace pv { +namespace dialogs { + +StoreProgress::StoreProgress(const QString &file_name, + const SigSession &session, QWidget *parent) : + QProgressDialog(tr("Saving..."), tr("Cancel"), 0, 0, parent), + _session(file_name.toStdString(), session) +{ + connect(&_session, SIGNAL(progress_updated()), + this, SLOT(on_progress_updated())); +} + +StoreProgress::~StoreProgress() +{ + _session.wait(); +} + +void StoreProgress::run() +{ + if (_session.start()) + show(); + else + show_error(); +} + +void StoreProgress::show_error() +{ + QMessageBox msg(parentWidget()); + msg.setText(tr("Failed to save session.")); + msg.setInformativeText(_session.error()); + msg.setStandardButtons(QMessageBox::Ok); + msg.setIcon(QMessageBox::Warning); + msg.exec(); +} + +void StoreProgress::closeEvent(QCloseEvent*) +{ + _session.cancel(); +} + +void StoreProgress::on_progress_updated() +{ + const std::pair p = _session.progress(); + assert(p.first <= p.second); + + setValue(p.first); + setMaximum(p.second); + + const QString err = _session.error(); + if (!err.isEmpty()) { + show_error(); + close(); + } + + if (p.first == p.second) + close(); +} + +} // dialogs +} // pv diff --git a/pv/dialogs/storeprogress.h b/pv/dialogs/storeprogress.h new file mode 100644 index 00000000..61d08d51 --- /dev/null +++ b/pv/dialogs/storeprogress.h @@ -0,0 +1,65 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2014 Joel Holdsworth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_H +#define PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_H + +#include + +#include + +#include + +#include + +namespace pv { + +class SigSession; + +namespace dialogs { + +class StoreProgress : public QProgressDialog +{ + Q_OBJECT + +public: + StoreProgress(const QString &file_name, const SigSession &session, + QWidget *parent = 0); + + virtual ~StoreProgress(); + + void run(); + +private: + void show_error(); + + void closeEvent(QCloseEvent*); + +private slots: + void on_progress_updated(); + +private: + pv::StoreSession _session; +}; + +} // dialogs +} // pv + +#endif // PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_H diff --git a/pv/mainwindow.cpp b/pv/mainwindow.cpp index 317f3c9a..ddaeeda9 100644 --- a/pv/mainwindow.cpp +++ b/pv/mainwindow.cpp @@ -41,6 +41,7 @@ #include "devicemanager.h" #include "dialogs/about.h" #include "dialogs/connect.h" +#include "dialogs/storeprogress.h" #include "toolbars/samplingbar.h" #include "view/logicsignal.h" #include "view/view.h" @@ -120,6 +121,14 @@ void MainWindow::setup_ui() action_open->setObjectName(QString::fromUtf8("actionOpen")); menu_file->addAction(action_open); + QAction *const action_save_as = new QAction(this); + action_save_as->setText(QApplication::translate( + "MainWindow", "&Save As...", 0, QApplication::UnicodeUTF8)); + action_save_as->setIcon(QIcon::fromTheme("document-save-as", + QIcon(":/icons/document-save-as.png"))); + action_save_as->setObjectName(QString::fromUtf8("actionSaveAs")); + menu_file->addAction(action_save_as); + menu_file->addSeparator(); QAction *const action_connect = new QAction(this); @@ -328,6 +337,24 @@ void MainWindow::on_actionOpen_triggered() load_file(file_name); } +void MainWindow::on_actionSaveAs_triggered() +{ + using pv::dialogs::StoreProgress; + + // Stop any currently running capture session + _session.stop_capture(); + + // Show the dialog + const QString file_name = QFileDialog::getSaveFileName( + this, tr("Save File"), "", tr("Sigrok Sessions (*.sr)")); + + if (file_name.isEmpty()) + return; + + StoreProgress *dlg = new StoreProgress(file_name, _session, this); + dlg->run(); +} + void MainWindow::on_actionConnect_triggered() { // Stop any currently running capture session diff --git a/pv/mainwindow.h b/pv/mainwindow.h index d5ac35ee..6f352f9f 100644 --- a/pv/mainwindow.h +++ b/pv/mainwindow.h @@ -81,6 +81,7 @@ private slots: const QString text, const QString info_text); void on_actionOpen_triggered(); + void on_actionSaveAs_triggered(); void on_actionQuit_triggered(); void on_actionConnect_triggered(); diff --git a/pv/storesession.cpp b/pv/storesession.cpp new file mode 100644 index 00000000..ebb4b0ed --- /dev/null +++ b/pv/storesession.cpp @@ -0,0 +1,195 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2014 Joel Holdsworth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "storesession.h" + +#include +#include +#include +#include + +using boost::dynamic_pointer_cast; +using boost::mutex; +using boost::shared_ptr; +using boost::thread; +using boost::lock_guard; +using std::deque; +using std::make_pair; +using std::min; +using std::pair; +using std::set; +using std::string; +using std::vector; + +namespace pv { + +const size_t StoreSession::BlockSize = 1024 * 1024; + +StoreSession::StoreSession(const std::string &file_name, + const SigSession &session) : + _file_name(file_name), + _session(session), + _units_stored(0), + _unit_count(0) +{ +} + +StoreSession::~StoreSession() +{ + wait(); +} + +pair StoreSession::progress() const +{ + lock_guard lock(_mutex); + return make_pair(_units_stored, _unit_count); +} + +const QString& StoreSession::error() const +{ + lock_guard lock(_mutex); + return _error; +} + +bool StoreSession::start() +{ + set< shared_ptr > data_set = + _session.get_data(); + const vector< shared_ptr > sigs = + _session.get_signals(); + + // Check we have logic data + if (data_set.empty() || sigs.empty()) { + _error = tr("No data to save."); + return false; + } + + if (data_set.size() > 1) { + _error = tr("PulseView currently only has support for " + "storing a single data stream."); + return false; + } + + // Get the logic data + //shared_ptr data; + if (!(data = dynamic_pointer_cast(*data_set.begin()))) { + _error = tr("PulseView currently only has support for " + "storing a logic data."); + return false; + } + + // Get the snapshot + const deque< shared_ptr > &snapshots = + data->get_snapshots(); + + if (snapshots.empty()) { + _error = tr("No snapshots to save."); + return false; + } + + const shared_ptr snapshot(snapshots.front()); + assert(snapshot); + + // Make a list of probes + char **const probes = new char*[sigs.size() + 1]; + for (size_t i = 0; i < sigs.size(); i++) { + shared_ptr sig(sigs[i]); + assert(sig); + probes[i] = strdup(sig->get_name().toUtf8().constData()); + } + probes[sigs.size()] = NULL; + + // Begin storing + if (sr_session_save_init(_file_name.c_str(), + data->samplerate(), probes) != SR_OK) { + _error = tr("Error while saving."); + return false; + } + + // Delete the probes array + for (size_t i = 0; i <= sigs.size(); i++) + free(probes[i]); + delete[] probes; + + _thread = boost::thread(&StoreSession::store_proc, this, snapshot); + return true; +} + +void StoreSession::wait() +{ + _thread.join(); +} + +void StoreSession::cancel() +{ + _thread.interrupt(); +} + +void StoreSession::store_proc(shared_ptr snapshot) +{ + assert(snapshot); + + uint64_t start_sample = 0; + + /// TODO: Wrap this in a std::unique_ptr when we transition to C++11 + uint8_t *const data = new uint8_t[BlockSize]; + assert(data); + + const int unit_size = snapshot->unit_size(); + assert(unit_size != 0); + + { + lock_guard lock(_mutex); + _unit_count = snapshot->get_sample_count(); + } + + const unsigned int samples_per_block = BlockSize / unit_size; + + while (!boost::this_thread::interruption_requested() && + start_sample < _unit_count) + { + progress_updated(); + + const uint64_t end_sample = min( + start_sample + samples_per_block, _unit_count); + snapshot->get_samples(data, start_sample, end_sample); + + if(sr_session_append(_file_name.c_str(), data, unit_size, + end_sample - start_sample) != SR_OK) + { + _error = tr("Error while saving."); + break; + } + + start_sample = end_sample; + + { + lock_guard lock(_mutex); + _units_stored = start_sample; + } + } + + progress_updated(); + + delete[] data; +} + +} // pv diff --git a/pv/storesession.h b/pv/storesession.h new file mode 100644 index 00000000..5ef92a33 --- /dev/null +++ b/pv/storesession.h @@ -0,0 +1,83 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2014 Joel Holdsworth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PULSEVIEW_PV_STORESESSION_H +#define PULSEVIEW_PV_STORESESSION_H + +#include + +#include + +#include + +#include + +namespace pv { + +class SigSession; + +namespace data { +class LogicSnapshot; +} + +class StoreSession : public QObject +{ + Q_OBJECT + +private: + static const size_t BlockSize; + +public: + StoreSession(const std::string &file_name, + const SigSession &session); + + ~StoreSession(); + + std::pair progress() const; + + const QString& error() const; + + bool start(); + + void wait(); + + void cancel(); + +private: + void store_proc(boost::shared_ptr snapshot); + +signals: + void progress_updated(); + +private: + const std::string _file_name; + const SigSession &_session; + + boost::thread _thread; + + mutable boost::mutex _mutex; + uint64_t _units_stored; + uint64_t _unit_count; + QString _error; +}; + +} // pv + +#endif // PULSEVIEW_PV_STORESESSION_H