Merge DecoderStack into DecodeSignal
authorSoeren Apel <soeren@apelpie.net>
Wed, 14 Jun 2017 06:30:43 +0000 (08:30 +0200)
committerSoeren Apel <soeren@apelpie.net>
Tue, 4 Jul 2017 21:14:36 +0000 (23:14 +0200)
Several changes make up this commit, which unfortunately
can't be separated:

1) Move decoder stack management from DecoderStack to
DecodeSignal, thereby making DecoderStack unnecessary

2) Change the decoder stack from std::list to an
std::vector for direct decoder access

3) Introduce logic_mux_thread which will take care
of muxing the individual SignalBases' logic data into
(cached) logic data that libsigrokdecode expects.
This is necessary as we can no longer do simple bit
mapping within a single logic data segment's logic
data as we now may feed from multiple logic data
segments at once

4) Refactored the creation of decode traces, making
it more streamlined and flexible while simplifying
the class interface

5) Refactored the auto-assignment of channels

6) Refactored is_decode_signal()

7) Reworked decode signal save/restore, allowing
proper handling and with the decoder stack now
being part of the signal, easier save/restore of
the stack and its settings

CMakeLists.txt
pv/data/decoderstack.cpp [deleted file]
pv/data/decoderstack.hpp [deleted file]
pv/data/decodesignal.cpp
pv/data/decodesignal.hpp
pv/data/signalbase.cpp
pv/data/signalbase.hpp
pv/session.cpp
pv/views/trace/decodetrace.cpp
test/CMakeLists.txt

index b51753b6f790e6e1ca7849c344c0e2b14b34a10c..39f0c07f57dd96fdc4396569b2596a357c616412 100644 (file)
@@ -335,7 +335,6 @@ if(ENABLE_DECODE)
        list(APPEND pulseview_SOURCES
                pv/binding/decoder.cpp
                pv/data/decodesignal.cpp
-               pv/data/decoderstack.cpp
                pv/data/decode/annotation.cpp
                pv/data/decode/decoder.cpp
                pv/data/decode/row.cpp
@@ -347,7 +346,6 @@ if(ENABLE_DECODE)
 
        list(APPEND pulseview_HEADERS
                pv/data/decodesignal.hpp
-               pv/data/decoderstack.hpp
                pv/views/trace/decodetrace.hpp
                pv/widgets/decodergroupbox.hpp
                pv/widgets/decodermenu.hpp
diff --git a/pv/data/decoderstack.cpp b/pv/data/decoderstack.cpp
deleted file mode 100644 (file)
index c7504f5..0000000
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <libsigrokdecode/libsigrokdecode.h>
-
-#include <stdexcept>
-
-#include <QDebug>
-
-#include "decoderstack.hpp"
-
-#include <pv/data/decode/annotation.hpp>
-#include <pv/data/decode/decoder.hpp>
-#include <pv/data/logic.hpp>
-#include <pv/data/logicsegment.hpp>
-#include <pv/session.hpp>
-#include <pv/views/trace/logicsignal.hpp>
-
-using std::lock_guard;
-using std::mutex;
-using std::unique_lock;
-using std::deque;
-using std::make_pair;
-using std::max;
-using std::min;
-using std::list;
-using std::shared_ptr;
-using std::make_shared;
-using std::vector;
-
-using boost::optional;
-
-using namespace pv::data::decode;
-
-namespace pv {
-namespace data {
-
-const double DecoderStack::DecodeMargin = 1.0;
-const double DecoderStack::DecodeThreshold = 0.2;
-const int64_t DecoderStack::DecodeChunkLength = 10 * 1024 * 1024;
-const unsigned int DecoderStack::DecodeNotifyPeriod = 1024;
-
-mutex DecoderStack::global_srd_mutex_;
-
-DecoderStack::DecoderStack(pv::Session &session,
-       const srd_decoder *const dec) :
-       session_(session),
-       start_time_(0),
-       samplerate_(0),
-       sample_count_(0),
-       frame_complete_(false),
-       samples_decoded_(0)
-{
-       connect(&session_, SIGNAL(frame_began()),
-               this, SLOT(on_new_frame()));
-       connect(&session_, SIGNAL(data_received()),
-               this, SLOT(on_data_received()));
-       connect(&session_, SIGNAL(frame_ended()),
-               this, SLOT(on_frame_ended()));
-
-       stack_.push_back(make_shared<decode::Decoder>(dec));
-}
-
-DecoderStack::~DecoderStack()
-{
-       if (decode_thread_.joinable()) {
-               interrupt_ = true;
-               input_cond_.notify_one();
-               decode_thread_.join();
-       }
-}
-
-const list< shared_ptr<decode::Decoder> >& DecoderStack::stack() const
-{
-       return stack_;
-}
-
-void DecoderStack::push(shared_ptr<decode::Decoder> decoder)
-{
-       assert(decoder);
-       stack_.push_back(decoder);
-}
-
-void DecoderStack::remove(int index)
-{
-       assert(index >= 0);
-       assert(index < (int)stack_.size());
-
-       // Find the decoder in the stack
-       auto iter = stack_.begin();
-       for (int i = 0; i < index; i++, iter++)
-               assert(iter != stack_.end());
-
-       // Delete the element
-       stack_.erase(iter);
-}
-
-double DecoderStack::samplerate() const
-{
-       return samplerate_;
-}
-
-const pv::util::Timestamp& DecoderStack::start_time() const
-{
-       return start_time_;
-}
-
-int64_t DecoderStack::samples_decoded() const
-{
-       lock_guard<mutex> decode_lock(output_mutex_);
-       return samples_decoded_;
-}
-
-vector<Row> DecoderStack::get_visible_rows() const
-{
-       lock_guard<mutex> lock(output_mutex_);
-
-       vector<Row> rows;
-
-       for (const shared_ptr<decode::Decoder> &dec : stack_) {
-               assert(dec);
-               if (!dec->shown())
-                       continue;
-
-               const srd_decoder *const decc = dec->decoder();
-               assert(dec->decoder());
-
-               // Add a row for the decoder if it doesn't have a row list
-               if (!decc->annotation_rows)
-                       rows.emplace_back(decc);
-
-               // Add the decoder rows
-               for (const GSList *l = decc->annotation_rows; l; l = l->next) {
-                       const srd_decoder_annotation_row *const ann_row =
-                               (srd_decoder_annotation_row *)l->data;
-                       assert(ann_row);
-                       rows.emplace_back(decc, ann_row);
-               }
-       }
-
-       return rows;
-}
-
-uint64_t DecoderStack::inc_annotation_count()
-{
-       return (annotation_count_++);
-}
-
-void DecoderStack::get_annotation_subset(
-       vector<pv::data::decode::Annotation> &dest,
-       const Row &row, uint64_t start_sample,
-       uint64_t end_sample) const
-{
-       lock_guard<mutex> lock(output_mutex_);
-
-       const auto iter = rows_.find(row);
-       if (iter != rows_.end())
-               (*iter).second.get_annotation_subset(dest,
-                       start_sample, end_sample);
-}
-
-QString DecoderStack::error_message()
-{
-       lock_guard<mutex> lock(output_mutex_);
-       return error_message_;
-}
-
-void DecoderStack::clear()
-{
-       sample_count_ = 0;
-       annotation_count_ = 0;
-       frame_complete_ = false;
-       samples_decoded_ = 0;
-       error_message_ = QString();
-       rows_.clear();
-       class_rows_.clear();
-}
-
-void DecoderStack::begin_decode()
-{
-       if (decode_thread_.joinable()) {
-               interrupt_ = true;
-               input_cond_.notify_one();
-               decode_thread_.join();
-       }
-
-       clear();
-
-       // Check that all decoders have the required channels
-       for (const shared_ptr<decode::Decoder> &dec : stack_)
-               if (!dec->have_required_channels()) {
-                       error_message_ = tr("One or more required channels "
-                               "have not been specified");
-                       return;
-               }
-
-       // Add classes
-       for (const shared_ptr<decode::Decoder> &dec : stack_) {
-               assert(dec);
-               const srd_decoder *const decc = dec->decoder();
-               assert(dec->decoder());
-
-               // Add a row for the decoder if it doesn't have a row list
-               if (!decc->annotation_rows)
-                       rows_[Row(decc)] = decode::RowData();
-
-               // Add the decoder rows
-               for (const GSList *l = decc->annotation_rows; l; l = l->next) {
-                       const srd_decoder_annotation_row *const ann_row =
-                               (srd_decoder_annotation_row *)l->data;
-                       assert(ann_row);
-
-                       const Row row(decc, ann_row);
-
-                       // Add a new empty row data object
-                       rows_[row] = decode::RowData();
-
-                       // Map out all the classes
-                       for (const GSList *ll = ann_row->ann_classes;
-                               ll; ll = ll->next)
-                               class_rows_[make_pair(decc,
-                                       GPOINTER_TO_INT(ll->data))] = row;
-               }
-       }
-
-       // We get the logic data of the first channel in the list.
-       // This works because we are currently assuming all
-       // logic signals have the same data/segment
-       pv::data::SignalBase *signalbase;
-       pv::data::Logic *data = nullptr;
-
-       for (const shared_ptr<decode::Decoder> &dec : stack_)
-               if (dec && !dec->channels().empty() &&
-                       ((signalbase = (*dec->channels().begin()).second.get())) &&
-                       ((data = signalbase->logic_data().get())))
-                       break;
-
-       if (!data)
-               return;
-
-       // Check we have a segment of data
-       const deque< shared_ptr<pv::data::LogicSegment> > &segments =
-               data->logic_segments();
-       if (segments.empty())
-               return;
-       segment_ = segments.front();
-
-       // Get the samplerate and start time
-       start_time_ = segment_->start_time();
-       samplerate_ = segment_->samplerate();
-       if (samplerate_ == 0.0)
-               samplerate_ = 1.0;
-
-       interrupt_ = false;
-       decode_thread_ = std::thread(&DecoderStack::decode_proc, this);
-}
-
-uint64_t DecoderStack::max_sample_count() const
-{
-       uint64_t max_sample_count = 0;
-
-       for (const auto& row : rows_)
-               max_sample_count = max(max_sample_count,
-                       row.second.get_max_sample());
-
-       return max_sample_count;
-}
-
-optional<int64_t> DecoderStack::wait_for_data() const
-{
-       unique_lock<mutex> input_lock(input_mutex_);
-
-       // Do wait if we decoded all samples but we're still capturing
-       // Do not wait if we're done capturing
-       while (!interrupt_ && !frame_complete_ &&
-               (samples_decoded_ >= sample_count_) &&
-               (session_.get_capture_state() != Session::Stopped)) {
-
-               input_cond_.wait(input_lock);
-       }
-
-       // Return value is valid if we're not aborting the decode,
-       return boost::make_optional(!interrupt_ &&
-               // and there's more work to do...
-               (samples_decoded_ < sample_count_ || !frame_complete_) &&
-               // and if the end of the data hasn't been reached yet
-               (!((samples_decoded_ >= sample_count_) && (session_.get_capture_state() == Session::Stopped))),
-               sample_count_);
-}
-
-void DecoderStack::decode_data(
-       const int64_t abs_start_samplenum, const int64_t sample_count, const unsigned int unit_size,
-       srd_session *const session)
-{
-       const unsigned int chunk_sample_count =
-               DecodeChunkLength / segment_->unit_size();
-
-       for (int64_t i = abs_start_samplenum; !interrupt_ && i < sample_count;
-                       i += chunk_sample_count) {
-
-               const int64_t chunk_end = min(
-                       i + chunk_sample_count, sample_count);
-               const uint8_t* chunk = segment_->get_samples(i, chunk_end);
-
-               if (srd_session_send(session, i, chunk_end, chunk,
-                               (chunk_end - i) * unit_size, unit_size) != SRD_OK) {
-                       error_message_ = tr("Decoder reported an error");
-                       delete[] chunk;
-                       break;
-               }
-               delete[] chunk;
-
-               {
-                       lock_guard<mutex> lock(output_mutex_);
-                       samples_decoded_ = chunk_end;
-               }
-       }
-}
-
-void DecoderStack::decode_proc()
-{
-       optional<int64_t> sample_count;
-       srd_session *session;
-       srd_decoder_inst *prev_di = nullptr;
-
-       assert(segment_);
-
-       // Prevent any other decode threads from accessing libsigrokdecode
-       lock_guard<mutex> srd_lock(global_srd_mutex_);
-
-       // Create the session
-       srd_session_new(&session);
-       assert(session);
-
-       // Create the decoders
-       const unsigned int unit_size = segment_->unit_size();
-
-       for (const shared_ptr<decode::Decoder> &dec : stack_) {
-               srd_decoder_inst *const di = dec->create_decoder_inst(session);
-
-               if (!di) {
-                       error_message_ = tr("Failed to create decoder instance");
-                       srd_session_destroy(session);
-                       return;
-               }
-
-               if (prev_di)
-                       srd_inst_stack (session, prev_di, di);
-
-               prev_di = di;
-       }
-
-       // Get the intial sample count
-       {
-               unique_lock<mutex> input_lock(input_mutex_);
-               sample_count = sample_count_ = segment_->get_sample_count();
-       }
-
-       // Start the session
-       srd_session_metadata_set(session, SRD_CONF_SAMPLERATE,
-               g_variant_new_uint64((uint64_t)samplerate_));
-
-       srd_pd_output_callback_add(session, SRD_OUTPUT_ANN,
-               DecoderStack::annotation_callback, this);
-
-       srd_session_start(session);
-
-       int64_t abs_start_samplenum = 0;
-       do {
-               decode_data(abs_start_samplenum, *sample_count, unit_size, session);
-               abs_start_samplenum = *sample_count;
-       } while (error_message_.isEmpty() && (sample_count = wait_for_data()));
-
-       // Make sure all annotations are known to the frontend
-       new_annotations();
-
-       // Destroy the session
-       srd_session_destroy(session);
-}
-
-void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder_stack)
-{
-       assert(pdata);
-       assert(decoder);
-
-       DecoderStack *const ds = (DecoderStack*)decoder_stack;
-       assert(ds);
-
-       lock_guard<mutex> lock(ds->output_mutex_);
-
-       const Annotation a(pdata);
-
-       // Find the row
-       assert(pdata->pdo);
-       assert(pdata->pdo->di);
-       const srd_decoder *const decc = pdata->pdo->di->decoder;
-       assert(decc);
-
-       auto row_iter = ds->rows_.end();
-
-       // Try looking up the sub-row of this class
-       const auto r = ds->class_rows_.find(make_pair(decc, a.format()));
-       if (r != ds->class_rows_.end())
-               row_iter = ds->rows_.find((*r).second);
-       else {
-               // Failing that, use the decoder as a key
-               row_iter = ds->rows_.find(Row(decc));
-       }
-
-       assert(row_iter != ds->rows_.end());
-       if (row_iter == ds->rows_.end()) {
-               qDebug() << "Unexpected annotation: decoder = " << decc <<
-                       ", format = " << a.format();
-               assert(false);
-               return;
-       }
-
-       // Add the annotation
-       (*row_iter).second.push_annotation(a);
-
-       // Notify the frontend every DecodeNotifyPeriod annotations
-       if (ds->inc_annotation_count() % DecodeNotifyPeriod == 0)
-               ds->new_annotations();
-}
-
-void DecoderStack::on_new_frame()
-{
-       begin_decode();
-}
-
-void DecoderStack::on_data_received()
-{
-       {
-               unique_lock<mutex> lock(input_mutex_);
-               if (segment_)
-                       sample_count_ = segment_->get_sample_count();
-       }
-       input_cond_.notify_one();
-}
-
-void DecoderStack::on_frame_ended()
-{
-       {
-               unique_lock<mutex> lock(input_mutex_);
-               if (segment_)
-                       frame_complete_ = true;
-       }
-       input_cond_.notify_one();
-}
-
-} // namespace data
-} // namespace pv
diff --git a/pv/data/decoderstack.hpp b/pv/data/decoderstack.hpp
deleted file mode 100644 (file)
index 37e4888..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 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, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_DATA_DECODERSTACK_HPP
-#define PULSEVIEW_PV_DATA_DECODERSTACK_HPP
-
-#include "signaldata.hpp"
-
-#include <atomic>
-#include <condition_variable>
-#include <list>
-#include <map>
-#include <memory>
-#include <thread>
-
-#include <boost/optional.hpp>
-
-#include <QObject>
-#include <QString>
-
-#include <pv/data/decode/row.hpp>
-#include <pv/data/decode/rowdata.hpp>
-#include <pv/util.hpp>
-
-using std::atomic;
-using std::condition_variable;
-using std::list;
-using std::map;
-using std::mutex;
-using std::pair;
-using std::shared_ptr;
-using std::vector;
-
-struct srd_decoder;
-struct srd_decoder_annotation_row;
-struct srd_channel;
-struct srd_proto_data;
-struct srd_session;
-
-namespace DecoderStackTest {
-struct TwoDecoderStack;
-}
-
-namespace pv {
-
-class Session;
-
-namespace view {
-class LogicSignal;
-}
-
-namespace data {
-
-class LogicSegment;
-
-namespace decode {
-class Annotation;
-class Decoder;
-}
-
-class Logic;
-
-class DecoderStack : public QObject
-{
-       Q_OBJECT
-
-private:
-       static const double DecodeMargin;
-       static const double DecodeThreshold;
-       static const int64_t DecodeChunkLength;
-       static const unsigned int DecodeNotifyPeriod;
-
-public:
-       DecoderStack(pv::Session &session, const srd_decoder *const dec);
-
-       virtual ~DecoderStack();
-
-       const list< shared_ptr<decode::Decoder> >& stack() const;
-       void push(shared_ptr<decode::Decoder> decoder);
-       void remove(int index);
-
-       double samplerate() const;
-
-       const pv::util::Timestamp& start_time() const;
-
-       int64_t samples_decoded() const;
-
-       vector<decode::Row> get_visible_rows() const;
-
-       /**
-        * Helper function for static annotation_callback(),
-        * must be public so the function can access it.
-        * Don't use from outside this class.
-        */
-       uint64_t inc_annotation_count();
-
-       /**
-        * Extracts sorted annotations between two period into a vector.
-        */
-       void get_annotation_subset(
-               vector<pv::data::decode::Annotation> &dest,
-               const decode::Row &row, uint64_t start_sample,
-               uint64_t end_sample) const;
-
-       QString error_message();
-
-       void clear();
-
-       uint64_t max_sample_count() const;
-
-       void begin_decode();
-
-private:
-       boost::optional<int64_t> wait_for_data() const;
-
-       void decode_data(const int64_t abs_start_samplenum, const int64_t sample_count,
-               const unsigned int unit_size, srd_session *const session);
-
-       void decode_proc();
-
-       static void annotation_callback(srd_proto_data *pdata, void *decoder_stack);
-
-private Q_SLOTS:
-       void on_new_frame();
-
-       void on_data_received();
-
-       void on_frame_ended();
-
-Q_SIGNALS:
-       void new_annotations();
-
-private:
-       pv::Session &session_;
-
-       pv::util::Timestamp start_time_;
-       double samplerate_;
-
-       /**
-        * This mutex prevents more than one thread from accessing
-        * libsigrokdecode concurrently.
-        * @todo A proper solution should be implemented to allow multiple
-        * decode operations in parallel.
-        */
-       static mutex global_srd_mutex_;
-
-       list< shared_ptr<decode::Decoder> > stack_;
-
-       shared_ptr<pv::data::LogicSegment> segment_;
-
-       mutable mutex input_mutex_;
-       mutable condition_variable input_cond_;
-       int64_t sample_count_, annotation_count_;
-       bool frame_complete_;
-
-       mutable mutex output_mutex_;
-       int64_t samples_decoded_;
-
-       map<const decode::Row, decode::RowData> rows_;
-
-       map<pair<const srd_decoder*, int>, decode::Row> class_rows_;
-
-       QString error_message_;
-
-       std::thread decode_thread_;
-       atomic<bool> interrupt_;
-
-       friend struct DecoderStackTest::TwoDecoderStack;
-};
-
-} // namespace data
-} // namespace pv
-
-#endif // PULSEVIEW_PV_DATA_DECODERSTACK_HPP
index 4061ef2760098ea32f810a98bd28e5535e35479f..14bef5d45bcecc6a89cad2345a6b15f145eecbe1 100644 (file)
@@ -19,6 +19,8 @@
 
 #include <limits>
 
+#include <QDebug>
+
 #include "logic.hpp"
 #include "logicsegment.hpp"
 #include "decodesignal.hpp"
 #include <pv/binding/decoder.hpp>
 #include <pv/data/decode/decoder.hpp>
 #include <pv/data/decode/row.hpp>
-#include <pv/data/decoderstack.hpp>
 #include <pv/session.hpp>
 
+using boost::optional;
+using std::lock_guard;
+using std::make_pair;
 using std::make_shared;
 using std::min;
 using std::shared_ptr;
+using std::unique_lock;
+using pv::data::decode::Annotation;
 using pv::data::decode::Decoder;
 using pv::data::decode::Row;
 
 namespace pv {
 namespace data {
 
-DecodeSignal::DecodeSignal(shared_ptr<pv::data::DecoderStack> decoder_stack,
-       const unordered_set< shared_ptr<data::SignalBase> > &all_signals) :
-       SignalBase(nullptr, SignalBase::DecodeChannel),
-       decoder_stack_(decoder_stack),
-       all_signals_(all_signals)
-{
-       set_name(QString::fromUtf8(decoder_stack_->stack().front()->decoder()->name));
+const double DecodeSignal::DecodeMargin = 1.0;
+const double DecodeSignal::DecodeThreshold = 0.2;
+const int64_t DecodeSignal::DecodeChunkLength = 10 * 1024 * 1024;
+const unsigned int DecodeSignal::DecodeNotifyPeriod = 1024;
 
-       update_channel_list();
-       auto_assign_signals();
+mutex DecodeSignal::global_srd_mutex_;
 
-       connect(decoder_stack_.get(), SIGNAL(new_annotations()),
-               this, SLOT(on_new_annotations()));
-}
 
-DecodeSignal::~DecodeSignal()
+DecodeSignal::DecodeSignal(pv::Session &session) :
+       SignalBase(nullptr, SignalBase::DecodeChannel),
+       session_(session),
+       logic_mux_data_invalid_(false),
+       start_time_(0),
+       samplerate_(0),
+       sample_count_(0),
+       annotation_count_(0),
+       samples_decoded_(0),
+       frame_complete_(false)
 {
+       connect(&session_, SIGNAL(capture_state_changed(int)),
+               this, SLOT(on_capture_state_changed(int)));
+       connect(&session_, SIGNAL(data_received()),
+               this, SLOT(on_data_received()));
+       connect(&session_, SIGNAL(frame_ended()),
+               this, SLOT(on_frame_ended()));
+
+       set_name(tr("Empty decoder signal"));
 }
 
-bool DecodeSignal::is_decode_signal() const
+DecodeSignal::~DecodeSignal()
 {
-       return true;
-}
+       if (decode_thread_.joinable()) {
+               decode_interrupt_ = true;
+               decode_input_cond_.notify_one();
+               decode_thread_.join();
+       }
 
-shared_ptr<pv::data::DecoderStack> DecodeSignal::decoder_stack() const
-{
-       return decoder_stack_;
+       if (logic_mux_thread_.joinable()) {
+               logic_mux_interrupt_ = true;
+               logic_mux_cond_.notify_one();
+               logic_mux_thread_.join();
+       }
 }
 
-const list< shared_ptr<Decoder> >& DecodeSignal::decoder_stack_list() const
+const vector< shared_ptr<Decoder> >& DecodeSignal::decoder_stack() const
 {
-       return decoder_stack_->stack();
+       return stack_;
 }
 
 void DecodeSignal::stack_decoder(srd_decoder *decoder)
 {
        assert(decoder);
-       assert(decoder_stack);
-       decoder_stack_->push(make_shared<data::decode::Decoder>(decoder));
+       stack_.push_back(make_shared<decode::Decoder>(decoder));
+
+       // Set name if this decoder is the first in the list
+       if (stack_.size() == 1)
+               set_name(QString::fromUtf8(decoder->name));
 
        // Include the newly created decode channels in the channel list
        update_channel_list();
 
        auto_assign_signals();
-       decoder_stack_->begin_decode();
+       begin_decode();
 }
 
 void DecodeSignal::remove_decoder(int index)
 {
-       decoder_stack_->remove(index);
+       assert(index >= 0);
+       assert(index < (int)stack_.size());
+
+       // Find the decoder in the stack
+       auto iter = stack_.begin();
+       for (int i = 0; i < index; i++, iter++)
+               assert(iter != stack_.end());
+
+       // Delete the element
+       stack_.erase(iter);
+
+       // Update channels and decoded data
        update_channel_list();
-       decoder_stack_->begin_decode();
+       begin_decode();
 }
 
 bool DecodeSignal::toggle_decoder_visibility(int index)
 {
-       const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
-
-       auto iter = stack.cbegin();
+       auto iter = stack_.cbegin();
        for (int i = 0; i < index; i++, iter++)
-               assert(iter != stack.end());
+               assert(iter != stack_.end());
 
        shared_ptr<Decoder> dec = *iter;
 
@@ -113,43 +146,127 @@ bool DecodeSignal::toggle_decoder_visibility(int index)
        return state;
 }
 
+void DecodeSignal::reset_decode()
+{
+       sample_count_ = 0;
+       annotation_count_ = 0;
+       frame_complete_ = false;
+       samples_decoded_ = 0;
+       error_message_ = QString();
+       rows_.clear();
+       class_rows_.clear();
+}
+
 void DecodeSignal::begin_decode()
 {
-       decoder_stack_->begin_decode();
+       if (decode_thread_.joinable()) {
+               decode_interrupt_ = true;
+               decode_input_cond_.notify_one();
+               decode_thread_.join();
+       }
+
+       if (logic_mux_thread_.joinable()) {
+               logic_mux_interrupt_ = true;
+               logic_mux_cond_.notify_one();
+               logic_mux_thread_.join();
+       }
+
+       reset_decode();
+
+       // Check that all decoders have the required channels
+       for (const shared_ptr<decode::Decoder> &dec : stack_)
+               if (!dec->have_required_channels()) {
+                       error_message_ = tr("One or more required channels "
+                               "have not been specified");
+                       return;
+               }
+
+       // Add annotation classes
+       for (const shared_ptr<decode::Decoder> &dec : stack_) {
+               assert(dec);
+               const srd_decoder *const decc = dec->decoder();
+               assert(dec->decoder());
+
+               // Add a row for the decoder if it doesn't have a row list
+               if (!decc->annotation_rows)
+                       rows_[Row(decc)] = decode::RowData();
+
+               // Add the decoder rows
+               for (const GSList *l = decc->annotation_rows; l; l = l->next) {
+                       const srd_decoder_annotation_row *const ann_row =
+                               (srd_decoder_annotation_row *)l->data;
+                       assert(ann_row);
+
+                       const Row row(decc, ann_row);
+
+                       // Add a new empty row data object
+                       rows_[row] = decode::RowData();
+
+                       // Map out all the classes
+                       for (const GSList *ll = ann_row->ann_classes;
+                               ll; ll = ll->next)
+                               class_rows_[make_pair(decc,
+                                       GPOINTER_TO_INT(ll->data))] = row;
+               }
+       }
+
+       // Make sure the logic output data is complete and up-to-date
+       logic_mux_thread_ = std::thread(&DecodeSignal::logic_mux_proc, this);
+
+       // Update the samplerate and start time
+       start_time_ = segment_->start_time();
+       samplerate_ = segment_->samplerate();
+       if (samplerate_ == 0.0)
+               samplerate_ = 1.0;
+
+       decode_interrupt_ = false;
+       decode_thread_ = std::thread(&DecodeSignal::decode_proc, this);
 }
 
 QString DecodeSignal::error_message() const
 {
-       return decoder_stack_->error_message();
+       lock_guard<mutex> lock(output_mutex_);
+       return error_message_;
 }
 
-const list<data::DecodeChannel> DecodeSignal::get_channels() const
+const vector<data::DecodeChannel> DecodeSignal::get_channels() const
 {
        return channels_;
 }
 
 void DecodeSignal::auto_assign_signals()
 {
+       bool new_assignment = false;
+
        // Try to auto-select channels that don't have signals assigned yet
        for (data::DecodeChannel &ch : channels_) {
                if (ch.assigned_signal)
                        continue;
 
-               for (shared_ptr<data::SignalBase> s : all_signals_)
-                       if (s->logic_data() && (ch.name.toLower().contains(s->name().toLower())))
+               for (shared_ptr<data::SignalBase> s : session_.signalbases())
+                       if (s->logic_data() && (ch.name.toLower().contains(s->name().toLower()))) {
                                ch.assigned_signal = s.get();
+                               new_assignment = true;
+                       }
+       }
+
+       if (new_assignment) {
+               logic_mux_data_invalid_ = true;
+               channels_updated();
        }
 }
 
 void DecodeSignal::assign_signal(const uint16_t channel_id, const SignalBase *signal)
 {
        for (data::DecodeChannel &ch : channels_)
-               if (ch.id == channel_id)
+               if (ch.id == channel_id) {
                        ch.assigned_signal = signal;
+                       logic_mux_data_invalid_ = true;
+               }
 
        channels_updated();
 
-       decoder_stack_->begin_decode();
+       begin_decode();
 }
 
 void DecodeSignal::set_initial_pin_state(const uint16_t channel_id, const int init_state)
@@ -160,17 +277,17 @@ void DecodeSignal::set_initial_pin_state(const uint16_t channel_id, const int in
 
        channels_updated();
 
-       decoder_stack_->begin_decode();
+       begin_decode();
 }
 
 double DecodeSignal::samplerate() const
 {
-       return decoder_stack_->samplerate();
+       return samplerate_;
 }
 
 const pv::util::Timestamp& DecodeSignal::start_time() const
 {
-       return decoder_stack_->start_time();
+       return start_time_;
 }
 
 int64_t DecodeSignal::get_working_sample_count() const
@@ -202,12 +319,38 @@ int64_t DecodeSignal::get_working_sample_count() const
 
 int64_t DecodeSignal::get_decoded_sample_count() const
 {
-       return decoder_stack_->samples_decoded();
+       lock_guard<mutex> decode_lock(output_mutex_);
+       return samples_decoded_;
 }
 
 vector<Row> DecodeSignal::visible_rows() const
 {
-       return decoder_stack_->get_visible_rows();
+       lock_guard<mutex> lock(output_mutex_);
+
+       vector<Row> rows;
+
+       for (const shared_ptr<decode::Decoder> &dec : stack_) {
+               assert(dec);
+               if (!dec->shown())
+                       continue;
+
+               const srd_decoder *const decc = dec->decoder();
+               assert(dec->decoder());
+
+               // Add a row for the decoder if it doesn't have a row list
+               if (!decc->annotation_rows)
+                       rows.emplace_back(decc);
+
+               // Add the decoder rows
+               for (const GSList *l = decc->annotation_rows; l; l = l->next) {
+                       const srd_decoder_annotation_row *const ann_row =
+                               (srd_decoder_annotation_row *)l->data;
+                       assert(ann_row);
+                       rows.emplace_back(decc, ann_row);
+               }
+       }
+
+       return rows;
 }
 
 void DecodeSignal::get_annotation_subset(
@@ -215,19 +358,42 @@ void DecodeSignal::get_annotation_subset(
        const decode::Row &row, uint64_t start_sample,
        uint64_t end_sample) const
 {
-       return decoder_stack_->get_annotation_subset(dest, row,
-               start_sample, end_sample);
+       lock_guard<mutex> lock(output_mutex_);
+
+       const auto iter = rows_.find(row);
+       if (iter != rows_.end())
+               (*iter).second.get_annotation_subset(dest,
+                       start_sample, end_sample);
+}
+
+void DecodeSignal::save_settings(QSettings &settings) const
+{
+       SignalBase::save_settings(settings);
+
+       // TODO Save decoder stack, channel mapping and decoder options
+}
+
+void DecodeSignal::restore_settings(QSettings &settings)
+{
+       SignalBase::restore_settings(settings);
+
+       // TODO Restore decoder stack, channel mapping and decoder options
+}
+
+uint64_t DecodeSignal::inc_annotation_count()
+{
+       return (annotation_count_++);
 }
 
 void DecodeSignal::update_channel_list()
 {
-       list<data::DecodeChannel> prev_channels = channels_;
+       vector<data::DecodeChannel> prev_channels = channels_;
        channels_.clear();
 
        uint16_t id = 0;
 
        // Copy existing entries, create new as needed
-       for (shared_ptr<Decoder> decoder : decoder_stack_->stack()) {
+       for (shared_ptr<Decoder> decoder : stack_) {
                const srd_decoder* srd_d = decoder->decoder();
                const GSList *l;
 
@@ -237,7 +403,7 @@ void DecodeSignal::update_channel_list()
                        bool ch_added = false;
 
                        // Copy but update ID if this channel was in the list before
-                       for (data::DecodeChannel ch : prev_channels)
+                       for (data::DecodeChannel &ch : prev_channels)
                                if (ch.pdch_ == pdch) {
                                        ch.id = id++;
                                        channels_.push_back(ch);
@@ -260,7 +426,7 @@ void DecodeSignal::update_channel_list()
                        bool ch_added = false;
 
                        // Copy but update ID if this channel was in the list before
-                       for (data::DecodeChannel ch : prev_channels)
+                       for (data::DecodeChannel &ch : prev_channels)
                                if (ch.pdch_ == pdch) {
                                        ch.id = id++;
                                        channels_.push_back(ch);
@@ -278,13 +444,203 @@ void DecodeSignal::update_channel_list()
                }
        }
 
+       // Invalidate the logic output data if the channel assignment changed
+       if (prev_channels.size() != channels_.size()) {
+               // The number of channels changed, there's definitely a difference
+               logic_mux_data_invalid_ = true;
+       } else {
+               // Same number but assignment may still differ, so compare all channels
+               for (size_t i = 0; i < channels_.size(); i++) {
+                       const data::DecodeChannel &p_ch = prev_channels[i];
+                       const data::DecodeChannel &ch = channels_[i];
+
+                       if ((p_ch.pdch_ != ch.pdch_) ||
+                               (p_ch.assigned_signal != ch.assigned_signal)) {
+                               logic_mux_data_invalid_ = true;
+                               break;
+                       }
+               }
+
+       }
+
        channels_updated();
 }
 
-void DecodeSignal::on_new_annotations()
+void DecodeSignal::logic_mux_proc()
 {
-       // Forward the signal to the frontend
+
+}
+
+optional<int64_t> DecodeSignal::wait_for_data() const
+{
+       unique_lock<mutex> input_lock(input_mutex_);
+
+       // Do wait if we decoded all samples but we're still capturing
+       // Do not wait if we're done capturing
+       while (!decode_interrupt_ && !frame_complete_ &&
+               (samples_decoded_ >= sample_count_) &&
+               (session_.get_capture_state() != Session::Stopped)) {
+
+               decode_input_cond_.wait(input_lock);
+       }
+
+       // Return value is valid if we're not aborting the decode,
+       return boost::make_optional(!decode_interrupt_ &&
+               // and there's more work to do...
+               (samples_decoded_ < sample_count_ || !frame_complete_) &&
+               // and if the end of the data hasn't been reached yet
+               (!((samples_decoded_ >= sample_count_) && (session_.get_capture_state() == Session::Stopped))),
+               sample_count_);
+}
+
+void DecodeSignal::decode_data(
+       const int64_t abs_start_samplenum, const int64_t sample_count,
+       srd_session *const session)
+{
+       const unsigned int unit_size = segment_->unit_size();
+       const unsigned int chunk_sample_count = DecodeChunkLength / unit_size;
+
+       for (int64_t i = abs_start_samplenum;
+               !decode_interrupt_ && (i < (abs_start_samplenum + sample_count));
+               i += chunk_sample_count) {
+
+               const int64_t chunk_end = min(i + chunk_sample_count,
+                       abs_start_samplenum + sample_count);
+
+               const uint8_t* chunk = segment_->get_samples(i, chunk_end);
+
+               if (srd_session_send(session, i, chunk_end, chunk,
+                               (chunk_end - i) * unit_size, unit_size) != SRD_OK) {
+                       error_message_ = tr("Decoder reported an error");
+                       delete[] chunk;
+                       break;
+               }
+               delete[] chunk;
+
+               {
+                       lock_guard<mutex> lock(output_mutex_);
+                       samples_decoded_ = chunk_end;
+               }
+       }
+}
+
+void DecodeSignal::decode_proc()
+{
+       optional<int64_t> sample_count;
+       srd_session *session;
+       srd_decoder_inst *prev_di = nullptr;
+
+       // Prevent any other decode threads from accessing libsigrokdecode
+       lock_guard<mutex> srd_lock(global_srd_mutex_);
+
+       // Create the session
+       srd_session_new(&session);
+       assert(session);
+
+       // Create the decoders
+       for (const shared_ptr<decode::Decoder> &dec : stack_) {
+               srd_decoder_inst *const di = dec->create_decoder_inst(session);
+
+               if (!di) {
+                       error_message_ = tr("Failed to create decoder instance");
+                       srd_session_destroy(session);
+                       return;
+               }
+
+               if (prev_di)
+                       srd_inst_stack(session, prev_di, di);
+
+               prev_di = di;
+       }
+
+       // Get the initial sample count
+       {
+               unique_lock<mutex> input_lock(input_mutex_);
+               sample_count = sample_count_ = get_working_sample_count();
+       }
+
+       // Start the session
+       srd_session_metadata_set(session, SRD_CONF_SAMPLERATE,
+               g_variant_new_uint64(samplerate_));
+
+       srd_pd_output_callback_add(session, SRD_OUTPUT_ANN,
+               DecodeSignal::annotation_callback, this);
+
+       srd_session_start(session);
+
+       int64_t abs_start_samplenum = 0;
+       do {
+               decode_data(abs_start_samplenum, *sample_count, session);
+               abs_start_samplenum = *sample_count;
+       } while (error_message_.isEmpty() && (sample_count = wait_for_data()));
+
+       // Make sure all annotations are known to the frontend
        new_annotations();
+
+       // Destroy the session
+       srd_session_destroy(session);
+}
+
+void DecodeSignal::annotation_callback(srd_proto_data *pdata, void *decode_signal)
+{
+       assert(pdata);
+       assert(decoder);
+
+       DecodeSignal *const ds = (DecodeSignal*)decode_signal;
+       assert(ds);
+
+       lock_guard<mutex> lock(ds->output_mutex_);
+
+       const decode::Annotation a(pdata);
+
+       // Find the row
+       assert(pdata->pdo);
+       assert(pdata->pdo->di);
+       const srd_decoder *const decc = pdata->pdo->di->decoder;
+       assert(decc);
+
+       auto row_iter = ds->rows_.end();
+
+       // Try looking up the sub-row of this class
+       const auto r = ds->class_rows_.find(make_pair(decc, a.format()));
+       if (r != ds->class_rows_.end())
+               row_iter = ds->rows_.find((*r).second);
+       else {
+               // Failing that, use the decoder as a key
+               row_iter = ds->rows_.find(Row(decc));
+       }
+
+       assert(row_iter != ds->rows_.end());
+       if (row_iter == ds->rows_.end()) {
+               qDebug() << "Unexpected annotation: decoder = " << decc <<
+                       ", format = " << a.format();
+               assert(false);
+               return;
+       }
+
+       // Add the annotation
+       (*row_iter).second.push_annotation(a);
+
+       // Notify the frontend every DecodeNotifyPeriod annotations
+       if (ds->inc_annotation_count() % DecodeNotifyPeriod == 0)
+               ds->new_annotations();
+}
+
+void DecodeSignal::on_capture_state_changed(int state)
+{
+       // If a new acquisition was started, we need to start decoding from scratch
+       if (state == Session::Running)
+               begin_decode();
+}
+
+void DecodeSignal::on_data_received()
+{
+       logic_mux_cond_.notify_one();
+}
+
+void DecodeSignal::on_frame_ended()
+{
+       logic_mux_cond_.notify_one();
 }
 
 } // namespace data
index 03b8d0af45e3f95d73ea4ac06656ec7d6e4eb478..9c8d382c808a74087f9566cae9535f66fd4ced78 100644 (file)
 #ifndef PULSEVIEW_PV_DATA_DECODESIGNAL_HPP
 #define PULSEVIEW_PV_DATA_DECODESIGNAL_HPP
 
+#include <atomic>
+#include <condition_variable>
 #include <unordered_set>
 #include <vector>
 
+#include <boost/optional.hpp>
+
+#include <QSettings>
 #include <QString>
 
 #include <libsigrokdecode/libsigrokdecode.h>
 
+#include <pv/data/decode/row.hpp>
+#include <pv/data/decode/rowdata.hpp>
 #include <pv/data/signalbase.hpp>
 #include <pv/util.hpp>
 
-using std::list;
+using std::atomic;
+using std::condition_variable;
+using std::map;
+using std::mutex;
+using std::pair;
 using std::unordered_set;
 using std::vector;
 using std::shared_ptr;
 
 namespace pv {
+class Session;
+
 namespace data {
 
 namespace decode {
@@ -44,8 +57,8 @@ class Decoder;
 class Row;
 }
 
-class DecoderStack;
 class Logic;
+class LogicSegment;
 class SignalBase;
 class SignalData;
 
@@ -64,23 +77,28 @@ class DecodeSignal : public SignalBase
 {
        Q_OBJECT
 
+private:
+       static const double DecodeMargin;
+       static const double DecodeThreshold;
+       static const int64_t DecodeChunkLength;
+       static const unsigned int DecodeNotifyPeriod;
+
 public:
-       DecodeSignal(shared_ptr<pv::data::DecoderStack> decoder_stack,
-               const unordered_set< shared_ptr<data::SignalBase> > &all_signals);
+       DecodeSignal(pv::Session &session);
        virtual ~DecodeSignal();
 
        bool is_decode_signal() const;
-       shared_ptr<data::DecoderStack> decoder_stack() const;
-       const list< shared_ptr<data::decode::Decoder> >& decoder_stack_list() const;
+       const vector< shared_ptr<data::decode::Decoder> >& decoder_stack() const;
 
        void stack_decoder(srd_decoder *decoder);
        void remove_decoder(int index);
        bool toggle_decoder_visibility(int index);
 
+       void reset_decode();
        void begin_decode();
        QString error_message() const;
 
-       const list<data::DecodeChannel> get_channels() const;
+       const vector<data::DecodeChannel> get_channels() const;
        void auto_assign_signals();
        void assign_signal(const uint16_t channel_id, const SignalBase *signal);
 
@@ -108,20 +126,74 @@ public:
                const decode::Row &row, uint64_t start_sample,
                uint64_t end_sample) const;
 
+       virtual void save_settings(QSettings &settings) const;
+
+       virtual void restore_settings(QSettings &settings);
+
+       /**
+        * Helper function for static annotation_callback(),
+        * must be public so the function can access it.
+        * Don't use from outside this class.
+        */
+       uint64_t inc_annotation_count();
+
 private:
        void update_channel_list();
 
+       void logic_mux_proc();
+
+       boost::optional<int64_t> wait_for_data() const;
+
+       void decode_data(const int64_t abs_start_samplenum, const int64_t sample_count,
+               srd_session *const session);
+
+       void decode_proc();
+
+       static void annotation_callback(srd_proto_data *pdata, void *decode_signal);
+
 Q_SIGNALS:
        void new_annotations();
        void channels_updated();
 
 private Q_SLOTS:
-       void on_new_annotations();
+       void on_capture_state_changed(int state);
+       void on_data_received();
+       void on_frame_ended();
 
 private:
-       shared_ptr<pv::data::DecoderStack> decoder_stack_;
-       const unordered_set< shared_ptr<data::SignalBase> > &all_signals_;
-       list<data::DecodeChannel> channels_;
+       pv::Session &session_;
+
+       vector<data::DecodeChannel> channels_;
+
+       shared_ptr<Logic> logic_mux_data_;
+       shared_ptr<LogicSegment> segment_;
+       bool logic_mux_data_invalid_;
+
+       pv::util::Timestamp start_time_;
+       double samplerate_;
+
+       int64_t sample_count_, annotation_count_, samples_decoded_;
+
+       vector< shared_ptr<decode::Decoder> > stack_;
+       map<const decode::Row, decode::RowData> rows_;
+       map<pair<const srd_decoder*, int>, decode::Row> class_rows_;
+
+       /**
+        * This mutex prevents more than one thread from accessing
+        * libsigrokdecode concurrently.
+        * @todo A proper solution should be implemented to allow multiple
+        * decode operations in parallel.
+        */
+       static mutex global_srd_mutex_;
+
+       mutable mutex input_mutex_, output_mutex_;
+       mutable condition_variable decode_input_cond_, logic_mux_cond_;
+       bool frame_complete_;
+
+       std::thread decode_thread_, logic_mux_thread_;
+       atomic<bool> decode_interrupt_, logic_mux_interrupt_;
+
+       QString error_message_;
 };
 
 } // namespace data
index 8693c58b37fb462d1546f7d0f9bf4966442b4368..bbb7fdc36382c82c76b8305c866a5c221454aeef 100644 (file)
@@ -200,14 +200,7 @@ void SignalBase::set_conversion_type(ConversionType t)
 #ifdef ENABLE_DECODE
 bool SignalBase::is_decode_signal() const
 {
-       // DecodeSignal class overrides this method, all others shall return false
-       return false;
-}
-
-shared_ptr<pv::data::DecoderStack> SignalBase::decoder_stack() const
-{
-       // DecodeSignal class overrides this method, all others shall return nothing
-       return nullptr;
+       return (channel_type_ == DecodeChannel);
 }
 #endif
 
index 97dbd8477d58dea4537e117a25946338f48f64c3..f70c934fb0eacdc8374b1d341ac50c17a96bef87 100644 (file)
@@ -148,14 +148,12 @@ public:
        void set_conversion_type(ConversionType t);
 
 #ifdef ENABLE_DECODE
-       virtual bool is_decode_signal() const;
-
-       virtual shared_ptr<pv::data::DecoderStack> decoder_stack() const;
+       bool is_decode_signal() const;
 #endif
 
-       void save_settings(QSettings &settings) const;
+       virtual void save_settings(QSettings &settings) const;
 
-       void restore_settings(QSettings &settings);
+       virtual void restore_settings(QSettings &settings);
 
 private:
        uint8_t convert_a2l_threshold(float threshold, float value);
index 07917e582ff2846d537fecaf755dfa4e125daae7..f96fc869ecad613816196072409c4d1458e198ad 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <QDebug>
 #include <QFileInfo>
 
 #include <cassert>
@@ -32,7 +33,6 @@
 #include "data/analog.hpp"
 #include "data/analogsegment.hpp"
 #include "data/decode/decoder.hpp"
-#include "data/decoderstack.hpp"
 #include "data/logic.hpp"
 #include "data/logicsegment.hpp"
 #include "data/signalbase.hpp"
@@ -173,7 +173,7 @@ void Session::save_settings(QSettings &settings) const
 {
        map<string, string> dev_info;
        list<string> key_list;
-       int stacks = 0, views = 0;
+       int decode_signals = 0, views = 0;
 
        if (device_) {
                shared_ptr<devices::HardwareDevice> hw_device =
@@ -217,14 +217,8 @@ void Session::save_settings(QSettings &settings) const
                for (shared_ptr<data::SignalBase> base : signalbases_) {
 #ifdef ENABLE_DECODE
                        if (base->is_decode_signal()) {
-                               shared_ptr<pv::data::DecoderStack> decoder_stack =
-                                               base->decoder_stack();
-                               shared_ptr<data::decode::Decoder> top_decoder =
-                                               decoder_stack->stack().front();
-
-                               settings.beginGroup("decoder_stack" + QString::number(stacks++));
-                               settings.setValue("id", top_decoder->decoder()->id);
-                               settings.setValue("name", top_decoder->decoder()->name);
+                               settings.beginGroup("decode_signal" + QString::number(decode_signals++));
+                               base->save_settings(settings);
                                settings.endGroup();
                        } else
 #endif
@@ -235,7 +229,7 @@ void Session::save_settings(QSettings &settings) const
                        }
                }
 
-               settings.setValue("decoder_stacks", stacks);
+               settings.setValue("decode_signals", decode_signals);
 
                // Save view states and their signal settings
                // Note: main_view must be saved as view0
@@ -319,14 +313,12 @@ void Session::restore_settings(QSettings &settings)
 
                // Restore decoders
 #ifdef ENABLE_DECODE
-               int stacks = settings.value("decoder_stacks").toInt();
-
-               for (int i = 0; i < stacks; i++) {
-                       settings.beginGroup("decoder_stack" + QString::number(i++));
-
-                       QString id = settings.value("id").toString();
-                       add_decoder(srd_decoder_get_by_id(id.toStdString().c_str()));
+               int decode_signals = settings.value("decode_signals").toInt();
 
+               for (int i = 0; i < decode_signals; i++) {
+                       settings.beginGroup("decode_signal" + QString::number(i++));
+                       // TODO Split up add_decoder() into add_decode_signal() and add_decoder(),
+                       // then call add_decode_signal() and signal->restore_settings() here
                        settings.endGroup();
                }
 #endif
@@ -658,35 +650,25 @@ bool Session::add_decoder(srd_decoder *const dec)
        if (!dec)
                return false;
 
-       map<const srd_channel*, shared_ptr<data::SignalBase> > channels;
-       shared_ptr<data::DecoderStack> decoder_stack;
-
        try {
-               // Create the decoder
-               decoder_stack = make_shared<data::DecoderStack>(*this, dec);
-
-               assert(decoder_stack);
-               assert(!decoder_stack->stack().empty());
-               assert(decoder_stack->stack().front());
-               decoder_stack->stack().front()->set_channels(channels);
-
                // Create the decode signal
                shared_ptr<data::DecodeSignal> signal =
-                       make_shared<data::DecodeSignal>(decoder_stack, signalbases_);
+                       make_shared<data::DecodeSignal>(*this);
 
                signalbases_.insert(signal);
 
+               // Add the decode signal to all views
                for (shared_ptr<views::ViewBase> view : views_)
                        view->add_decode_signal(signal);
+
+               // Add decoder
+               signal->stack_decoder(dec);
        } catch (runtime_error e) {
                return false;
        }
 
        signals_changed();
 
-       // Do an initial decode
-       decoder_stack->begin_decode();
-
        return true;
 }
 
index ece5f36c0574978b4edcbfb953d79e023645d2a3..d8f04c582ef5a6c9c76696e344e034a52db6bace 100644 (file)
@@ -54,10 +54,8 @@ extern "C" {
 #include <pv/widgets/decodermenu.hpp>
 
 using std::all_of;
-using std::list;
 using std::make_pair;
 using std::max;
-using std::make_pair;
 using std::map;
 using std::min;
 using std::out_of_range;
@@ -304,8 +302,7 @@ void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
        init_state_map_.clear();
        decoder_forms_.clear();
 
-       const list< shared_ptr<Decoder> > &stack =
-               decode_signal_->decoder_stack_list();
+       const vector< shared_ptr<Decoder> > &stack = decode_signal_->decoder_stack();
 
        if (stack.empty()) {
                QLabel *const l = new QLabel(
@@ -776,7 +773,7 @@ void DecodeTrace::create_decoder_form(int index,
        QFormLayout *const decoder_form = new QFormLayout;
        group->add_layout(decoder_form);
 
-       const list<DecodeChannel> channels = decode_signal_->get_channels();
+       const vector<DecodeChannel> channels = decode_signal_->get_channels();
 
        // Add the channels
        for (DecodeChannel ch : channels) {
index 896ba028f7bc5f13d16200ffb5351d3dd0bfbe4d..e43d30dc9d3e888967f2900cc2dc25430b447fbc 100644 (file)
@@ -158,7 +158,6 @@ if(ENABLE_DECODE)
        list(APPEND pulseview_TEST_SOURCES
                ${PROJECT_SOURCE_DIR}/pv/binding/decoder.cpp
                ${PROJECT_SOURCE_DIR}/pv/data/decodesignal.cpp
-               ${PROJECT_SOURCE_DIR}/pv/data/decoderstack.cpp
                ${PROJECT_SOURCE_DIR}/pv/data/decode/annotation.cpp
                ${PROJECT_SOURCE_DIR}/pv/data/decode/decoder.cpp
                ${PROJECT_SOURCE_DIR}/pv/data/decode/row.cpp
@@ -166,12 +165,10 @@ if(ENABLE_DECODE)
                ${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.cpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.cpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.cpp
-               data/decoderstack.cpp
        )
 
        list(APPEND pulseview_TEST_HEADERS
                ${PROJECT_SOURCE_DIR}/pv/data/decodesignal.hpp
-               ${PROJECT_SOURCE_DIR}/pv/data/decoderstack.hpp
                ${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.hpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.hpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.hpp