2 * This file is part of the PulseView project.
4 * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "storesession.hpp"
26 #include <pv/data/analog.hpp>
27 #include <pv/data/analogsegment.hpp>
28 #include <pv/data/logic.hpp>
29 #include <pv/data/logicsegment.hpp>
30 #include <pv/data/signalbase.hpp>
31 #include <pv/devicemanager.hpp>
32 #include <pv/devices/device.hpp>
33 #include <pv/globalsettings.hpp>
34 #include <pv/session.hpp>
36 #include <libsigrokcxx/libsigrokcxx.hpp>
40 using std::lock_guard
;
46 using std::shared_ptr
;
50 using Glib::VariantBase
;
52 using sigrok::ConfigKey
;
54 using sigrok::OutputFormat
;
55 using sigrok::OutputFlag
;
59 const size_t StoreSession::BlockSize
= 10 * 1024 * 1024;
61 StoreSession::StoreSession(const string
&file_name
,
62 const shared_ptr
<OutputFormat
> &output_format
,
63 const map
<string
, VariantBase
> &options
,
64 const pair
<uint64_t, uint64_t> sample_range
,
65 const Session
&session
) :
66 file_name_(file_name
),
67 output_format_(output_format
),
69 sample_range_(sample_range
),
77 StoreSession::~StoreSession()
82 pair
<int, int> StoreSession::progress() const
84 return make_pair(units_stored_
.load(), unit_count_
.load());
87 const QString
& StoreSession::error() const
89 lock_guard
<mutex
> lock(mutex_
);
93 bool StoreSession::start()
95 const vector
< shared_ptr
<data::SignalBase
> > sigs(session_
.signalbases());
97 shared_ptr
<data::Segment
> any_segment
;
98 shared_ptr
<data::LogicSegment
> lsegment
;
99 vector
< shared_ptr
<data::SignalBase
> > achannel_list
;
100 vector
< shared_ptr
<data::AnalogSegment
> > asegment_list
;
102 for (const shared_ptr
<data::SignalBase
>& signal
: sigs
) {
103 if (!signal
->enabled())
106 if (signal
->type() == data::SignalBase::LogicChannel
) {
107 // All logic channels share the same data segments
108 shared_ptr
<data::Logic
> ldata
= signal
->logic_data();
110 const deque
< shared_ptr
<data::LogicSegment
> > &lsegments
=
111 ldata
->logic_segments();
113 if (lsegments
.empty()) {
114 error_
= tr("Can't save logic channel without data.");
118 lsegment
= lsegments
.front();
119 any_segment
= lsegment
;
122 if (signal
->type() == data::SignalBase::AnalogChannel
) {
123 // Each analog channel has its own segments
124 shared_ptr
<data::Analog
> adata
= signal
->analog_data();
126 const deque
< shared_ptr
<data::AnalogSegment
> > &asegments
=
127 adata
->analog_segments();
129 if (asegments
.empty()) {
130 error_
= tr("Can't save analog channel without data.");
134 asegment_list
.push_back(asegments
.front());
135 any_segment
= asegments
.front();
137 achannel_list
.push_back(signal
);
142 error_
= tr("No channels enabled.");
146 // Check whether the user wants to export a certain sample range
149 if (sample_range_
.first
== sample_range_
.second
) {
150 // No sample range specified, save everything we have
152 sample_count_
= any_segment
->get_sample_count();
154 if (sample_range_
.first
> sample_range_
.second
) {
155 start_sample_
= sample_range_
.second
;
156 end_sample
= min(sample_range_
.first
, any_segment
->get_sample_count());
157 sample_count_
= end_sample
- start_sample_
;
159 start_sample_
= sample_range_
.first
;
160 end_sample
= min(sample_range_
.second
, any_segment
->get_sample_count());
161 sample_count_
= end_sample
- start_sample_
;
165 // Make sure the sample range is valid
166 if (start_sample_
> any_segment
->get_sample_count()) {
167 error_
= tr("Can't save range without sample data.");
173 const auto context
= session_
.device_manager().context();
174 auto device
= session_
.device()->device();
176 map
<string
, Glib::VariantBase
> options
= options_
;
178 if (!output_format_
->test_flag(OutputFlag::INTERNAL_IO_HANDLING
))
179 output_stream_
.open(file_name_
, ios_base::binary
|
180 ios_base::trunc
| ios_base::out
);
182 output_
= output_format_
->create_output(file_name_
, device
, options
);
183 auto meta
= context
->create_meta_packet(
184 {{ConfigKey::SAMPLERATE
, Glib::Variant
<guint64
>::create(
185 any_segment
->samplerate())}});
186 output_
->receive(meta
);
187 } catch (Error
& error
) {
188 error_
= tr("Error while saving: ") + error
.what();
192 thread_
= std::thread(&StoreSession::store_proc
, this,
193 achannel_list
, asegment_list
, lsegment
);
195 // Save session setup if we're saving to srzip and the user wants it
196 GlobalSettings settings
;
197 bool save_with_setup
= settings
.value(GlobalSettings::Key_General_SaveWithSetup
).toBool();
199 if ((output_format_
->name() == "srzip") && (save_with_setup
)) {
200 QString setup_file_name
= QString::fromStdString(file_name_
);
201 setup_file_name
.truncate(setup_file_name
.lastIndexOf('.'));
202 setup_file_name
.append(".pvs");
204 QSettings
settings_storage(setup_file_name
, QSettings::IniFormat
);
205 session_
.save_setup(settings_storage
);
211 void StoreSession::wait()
213 if (thread_
.joinable())
217 void StoreSession::cancel()
222 void StoreSession::store_proc(vector
< shared_ptr
<data::SignalBase
> > achannel_list
,
223 vector
< shared_ptr
<data::AnalogSegment
> > asegment_list
,
224 shared_ptr
<data::LogicSegment
> lsegment
)
226 unsigned progress_scale
= 0;
230 unsigned int lsamples_per_block
= INT_MAX
;
231 unsigned int asamples_per_block
= INT_MAX
;
233 if (!asegment_list
.empty()) {
234 // We assume all analog channels use the sample unit size
235 aunit_size
= asegment_list
.front()->unit_size();
236 asamples_per_block
= BlockSize
/ aunit_size
;
239 lunit_size
= lsegment
->unit_size();
240 lsamples_per_block
= BlockSize
/ lunit_size
;
243 // Qt needs the progress values to fit inside an int. If they would
244 // not, scale the current and max values down until they do.
245 while ((sample_count_
>> progress_scale
) > INT_MAX
)
248 unit_count_
= sample_count_
>> progress_scale
;
250 const unsigned int samples_per_block
=
251 min(asamples_per_block
, lsamples_per_block
);
253 const auto context
= session_
.device_manager().context();
254 while (!interrupt_
&& sample_count_
) {
257 const uint64_t packet_len
=
258 min((uint64_t)samples_per_block
, sample_count_
);
261 for (unsigned int i
= 0; i
< achannel_list
.size(); i
++) {
262 shared_ptr
<sigrok::Channel
> achannel
= (achannel_list
.at(i
))->channel();
263 shared_ptr
<data::AnalogSegment
> asegment
= asegment_list
.at(i
);
265 float *adata
= new float[packet_len
];
266 asegment
->get_samples(start_sample_
, start_sample_
+ packet_len
, adata
);
268 auto analog
= context
->create_analog_packet(
269 vector
<shared_ptr
<sigrok::Channel
> >{achannel
},
270 (float *)adata
, packet_len
,
271 sigrok::Quantity::VOLTAGE
, sigrok::Unit::VOLT
,
272 vector
<const sigrok::QuantityFlag
*>());
273 const string adata_str
= output_
->receive(analog
);
275 if (output_stream_
.is_open())
276 output_stream_
<< adata_str
;
282 const size_t data_size
= packet_len
* lunit_size
;
283 uint8_t* ldata
= new uint8_t[data_size
];
284 lsegment
->get_samples(start_sample_
, start_sample_
+ packet_len
, ldata
);
286 auto logic
= context
->create_logic_packet((void*)ldata
, data_size
, lunit_size
);
287 const string ldata_str
= output_
->receive(logic
);
289 if (output_stream_
.is_open())
290 output_stream_
<< ldata_str
;
294 } catch (Error
& error
) {
295 error_
= tr("Error while saving: ") + error
.what();
299 sample_count_
-= packet_len
;
300 start_sample_
+= packet_len
;
301 units_stored_
= unit_count_
- (sample_count_
>> progress_scale
);
305 auto dfend
= context
->create_end_packet();
306 const string ldata_str
= output_
->receive(dfend
);
307 if (output_stream_
.is_open())
308 output_stream_
<< ldata_str
;
309 } catch (Error
& error
) {
310 error_
= tr("Error while saving: ") + error
.what();
313 // Zeroing the progress variables indicates completion
314 units_stored_
= unit_count_
= 0;
320 output_stream_
.close();