Added support for save
authorJoel Holdsworth <joel@airwebreathe.org.uk>
Thu, 9 Jan 2014 18:58:31 +0000 (18:58 +0000)
committerJoel Holdsworth <joel@airwebreathe.org.uk>
Sat, 11 Jan 2014 09:23:34 +0000 (09:23 +0000)
CMakeLists.txt
icons/document-save-as.png [new file with mode: 0644]
pulseview.qrc
pv/data/logicsnapshot.cpp
pv/dialogs/storeprogress.cpp [new file with mode: 0644]
pv/dialogs/storeprogress.h [new file with mode: 0644]
pv/mainwindow.cpp
pv/mainwindow.h
pv/storesession.cpp [new file with mode: 0644]
pv/storesession.h [new file with mode: 0644]

index 6b8f9fa8317fdb8128cd50772d4ab3dc3937d191..94d32a41f1d53e6b892f81c6150f57b106bebba6 100644 (file)
@@ -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 (file)
index 0000000..0f7b299
Binary files /dev/null and b/icons/document-save-as.png differ
index d2e1c139c9afba70bed3f1f89c9631713a56f4b6..a18d655914d938dce3ff6f4a919aae4a06feb70f 100644 (file)
@@ -4,6 +4,7 @@
        <file>icons/configure.png</file>
        <file>icons/decoder-delete.svg</file>
        <file>icons/document-open.png</file>
+       <file>icons/document-save-as.png</file>
        <file>icons/probes.svg</file>
        <file>icons/sigrok-logo-notext.png</file>
        <file>icons/status-green.svg</file>
index f2fa81bcdde675fa9811977a32261be129687b75..177c66279bf0a1fa079f82b3da0d22a8f537ee86 100644 (file)
@@ -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<recursive_mutex> lock(_mutex);
diff --git a/pv/dialogs/storeprogress.cpp b/pv/dialogs/storeprogress.cpp
new file mode 100644 (file)
index 0000000..e0f670d
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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<uint64_t, uint64_t> 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 (file)
index 0000000..61d08d5
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 <set>
+
+#include <boost/shared_ptr.hpp>
+
+#include <QProgressDialog>
+
+#include <pv/storesession.h>
+
+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
index 317f3c9a701383793d869f5fda7e6a8aee42eea4..ddaeeda977c5ba8ad42769179fd084fa78d889c0 100644 (file)
@@ -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
index d5ac35ee92c68fc92dcf14651efa91167702a6d9..6f352f9f2cbcab545fd0980af1a215ddaad05f6a 100644 (file)
@@ -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 (file)
index 0000000..ebb4b0e
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 <pv/sigsession.h>
+#include <pv/data/logic.h>
+#include <pv/data/logicsnapshot.h>
+#include <pv/view/signal.h>
+
+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<uint64_t, uint64_t> StoreSession::progress() const
+{
+       lock_guard<mutex> lock(_mutex);
+       return make_pair(_units_stored, _unit_count);
+}
+
+const QString& StoreSession::error() const
+{
+       lock_guard<mutex> lock(_mutex);
+       return _error;
+}
+
+bool StoreSession::start()
+{
+       set< shared_ptr<data::SignalData> > data_set =
+               _session.get_data();
+       const vector< shared_ptr<view::Signal> > 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::SignalData
+       shared_ptr<data::Logic> data;
+       if (!(data = dynamic_pointer_cast<data::Logic>(*data_set.begin()))) {
+               _error = tr("PulseView currently only has support for "
+                       "storing a logic data.");
+               return false;
+       }
+
+       // Get the snapshot
+       const deque< shared_ptr<data::LogicSnapshot> > &snapshots =
+               data->get_snapshots();
+
+       if (snapshots.empty()) {
+               _error = tr("No snapshots to save.");
+               return false;
+       }
+
+       const shared_ptr<data::LogicSnapshot> 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<view::Signal> 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<data::LogicSnapshot> 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<mutex> 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<mutex> 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 (file)
index 0000000..5ef92a3
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 <stdint.h>
+
+#include <string>
+
+#include <boost/thread.hpp>
+
+#include <QObject>
+
+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<uint64_t, uint64_t> progress() const;
+
+       const QString& error() const;
+
+       bool start();
+
+       void wait();
+
+       void cancel();
+
+private:
+       void store_proc(boost::shared_ptr<pv::data::LogicSnapshot> 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