mirror of https://github.com/tstack/lnav
[spectro] add a summary overlay to the selected position and show log messages in a panel
parent
cec6c3a225
commit
52fe2db376
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lnav_bus_hh
|
||||
#define lnav_bus_hh
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "lnav_log.hh"
|
||||
|
||||
template<typename T>
|
||||
class bus {
|
||||
public:
|
||||
bus() = default;
|
||||
|
||||
virtual ~bus() { require(this->b_components.empty()); }
|
||||
|
||||
bus(const bus<T>&) = delete;
|
||||
|
||||
void attach(T* component)
|
||||
{
|
||||
this->b_components.template emplace_back(component);
|
||||
}
|
||||
|
||||
void detach(T* component)
|
||||
{
|
||||
auto iter = std::find(
|
||||
this->b_components.begin(), this->b_components.end(), component);
|
||||
require(iter != this->b_components.end());
|
||||
|
||||
this->b_components.erase(iter);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<T*> b_components;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "pollable.hh"
|
||||
|
||||
#include "base/lnav_log.hh"
|
||||
|
||||
pollable::pollable(std::shared_ptr<pollable_supervisor> supervisor,
|
||||
category cat)
|
||||
: p_supervisor(supervisor), p_category(cat)
|
||||
{
|
||||
log_debug("pollable attach %p to %p", this, this->p_supervisor.get());
|
||||
this->p_supervisor->attach(this);
|
||||
}
|
||||
|
||||
pollable::~pollable()
|
||||
{
|
||||
log_debug("pollable detach %p from %p", this, this->p_supervisor.get());
|
||||
this->p_supervisor->detach(this);
|
||||
}
|
||||
|
||||
pollable_supervisor::update_result
|
||||
pollable_supervisor::update_poll_set(std::vector<struct pollfd>& pollfds)
|
||||
{
|
||||
update_result retval;
|
||||
size_t old_size = pollfds.size();
|
||||
|
||||
for (auto& pol : this->b_components) {
|
||||
pol->update_poll_set(pollfds);
|
||||
switch (pol->get_category()) {
|
||||
case pollable::category::background:
|
||||
retval.ur_background += pollfds.size() - old_size;
|
||||
break;
|
||||
case pollable::category::interactive:
|
||||
retval.ur_interactive += pollfds.size() - old_size;
|
||||
break;
|
||||
}
|
||||
old_size = pollfds.size();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
pollable_supervisor::check_poll_set(const std::vector<struct pollfd>& pollfds)
|
||||
{
|
||||
std::vector<pollable*> visited;
|
||||
auto found_new = false;
|
||||
|
||||
// TODO move this loop into the superclass
|
||||
do {
|
||||
found_new = false;
|
||||
for (auto* pol : this->b_components) {
|
||||
if (std::find(visited.begin(), visited.end(), pol) == visited.end())
|
||||
{
|
||||
visited.emplace_back(pol);
|
||||
pol->check_poll_set(pollfds);
|
||||
found_new = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (found_new);
|
||||
}
|
||||
|
||||
size_t
|
||||
pollable_supervisor::count(pollable::category cat)
|
||||
{
|
||||
size_t retval = 0;
|
||||
|
||||
for (const auto* pol : this->b_components) {
|
||||
if (pol->get_category() == cat) {
|
||||
retval += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lnav_pollable_hh
|
||||
#define lnav_pollable_hh
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
#include "base/bus.hh"
|
||||
|
||||
class pollable_supervisor;
|
||||
|
||||
class pollable {
|
||||
public:
|
||||
enum class category {
|
||||
background,
|
||||
interactive,
|
||||
};
|
||||
|
||||
pollable(std::shared_ptr<pollable_supervisor> supervisor, category cat);
|
||||
|
||||
pollable(const pollable&) = delete;
|
||||
|
||||
virtual ~pollable();
|
||||
|
||||
category get_category() const { return this->p_category; }
|
||||
|
||||
virtual void update_poll_set(std::vector<struct pollfd>& pollfds) = 0;
|
||||
|
||||
virtual void check_poll_set(const std::vector<struct pollfd>& pollfds) = 0;
|
||||
|
||||
private:
|
||||
std::shared_ptr<pollable_supervisor> p_supervisor;
|
||||
const category p_category;
|
||||
};
|
||||
|
||||
class pollable_supervisor : public bus<pollable> {
|
||||
public:
|
||||
struct update_result {
|
||||
size_t ur_background{0};
|
||||
size_t ur_interactive{0};
|
||||
};
|
||||
|
||||
update_result update_poll_set(std::vector<struct pollfd>& pollfds);
|
||||
|
||||
void check_poll_set(const std::vector<struct pollfd>& pollfds);
|
||||
|
||||
size_t count(pollable::category cat);
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,397 @@
|
||||
/**
|
||||
* Copyright (c) 2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "spectro_impls.hh"
|
||||
|
||||
#include "lnav.hh"
|
||||
#include "logfile_sub_source.hh"
|
||||
|
||||
class filtered_sub_source : public text_sub_source {
|
||||
public:
|
||||
size_t text_line_count() override { return this->fss_lines.size(); }
|
||||
|
||||
void text_value_for_line(textview_curses& tc,
|
||||
int line,
|
||||
std::string& value_out,
|
||||
line_flags_t flags) override
|
||||
{
|
||||
this->fss_delegate->text_value_for_line(
|
||||
tc, this->fss_lines[line], value_out, flags);
|
||||
}
|
||||
|
||||
size_t text_size_for_line(textview_curses& tc,
|
||||
int line,
|
||||
line_flags_t raw) override
|
||||
{
|
||||
return this->fss_delegate->text_size_for_line(
|
||||
tc, this->fss_lines[line], raw);
|
||||
}
|
||||
|
||||
void text_attrs_for_line(textview_curses& tc,
|
||||
int line,
|
||||
string_attrs_t& value_out) override
|
||||
{
|
||||
this->fss_delegate->text_attrs_for_line(
|
||||
tc, this->fss_lines[line], value_out);
|
||||
}
|
||||
|
||||
text_sub_source* fss_delegate;
|
||||
std::vector<vis_line_t> fss_lines;
|
||||
};
|
||||
|
||||
log_spectro_value_source::log_spectro_value_source(intern_string_t colname)
|
||||
: lsvs_colname(colname)
|
||||
{
|
||||
this->update_stats();
|
||||
}
|
||||
|
||||
void
|
||||
log_spectro_value_source::update_stats()
|
||||
{
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
|
||||
this->lsvs_begin_time = 0;
|
||||
this->lsvs_end_time = 0;
|
||||
this->lsvs_stats.clear();
|
||||
for (auto& ls : lss) {
|
||||
auto* lf = ls->get_file_ptr();
|
||||
|
||||
if (lf == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto format = lf->get_format();
|
||||
const auto* stats = format->stats_for_value(this->lsvs_colname);
|
||||
|
||||
if (stats == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ll = lf->begin();
|
||||
|
||||
if (this->lsvs_begin_time == 0
|
||||
|| ll->get_time() < this->lsvs_begin_time) {
|
||||
this->lsvs_begin_time = ll->get_time();
|
||||
}
|
||||
ll = lf->end();
|
||||
--ll;
|
||||
if (ll->get_time() > this->lsvs_end_time) {
|
||||
this->lsvs_end_time = ll->get_time();
|
||||
}
|
||||
|
||||
this->lsvs_found = true;
|
||||
this->lsvs_stats.merge(*stats);
|
||||
}
|
||||
|
||||
if (this->lsvs_begin_time) {
|
||||
time_t filtered_begin_time = lss.find_line(lss.at(0_vl))->get_time();
|
||||
time_t filtered_end_time
|
||||
= lss.find_line(lss.at(vis_line_t(lss.text_line_count() - 1)))
|
||||
->get_time();
|
||||
|
||||
if (filtered_begin_time > this->lsvs_begin_time) {
|
||||
this->lsvs_begin_time = filtered_begin_time;
|
||||
}
|
||||
if (filtered_end_time < this->lsvs_end_time) {
|
||||
this->lsvs_end_time = filtered_end_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
log_spectro_value_source::spectro_bounds(spectrogram_bounds& sb_out)
|
||||
{
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
|
||||
if (lss.text_line_count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->update_stats();
|
||||
|
||||
sb_out.sb_begin_time = this->lsvs_begin_time;
|
||||
sb_out.sb_end_time = this->lsvs_end_time;
|
||||
sb_out.sb_min_value_out = this->lsvs_stats.lvs_min_value;
|
||||
sb_out.sb_max_value_out = this->lsvs_stats.lvs_max_value;
|
||||
sb_out.sb_count = this->lsvs_stats.lvs_count;
|
||||
}
|
||||
|
||||
void
|
||||
log_spectro_value_source::spectro_row(spectrogram_request& sr,
|
||||
spectrogram_row& row_out)
|
||||
{
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
auto begin_line = lss.find_from_time(sr.sr_begin_time).value_or(0_vl);
|
||||
auto end_line
|
||||
= lss.find_from_time(sr.sr_end_time).value_or(lss.text_line_count());
|
||||
|
||||
for (const auto& msg_info : lss.window_at(begin_line, end_line)) {
|
||||
const auto& ll = msg_info.get_logline();
|
||||
if (ll.get_time() >= sr.sr_end_time) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& values = msg_info.get_values();
|
||||
auto lv_iter = find_if(values.begin(),
|
||||
values.end(),
|
||||
logline_value_cmp(&this->lsvs_colname));
|
||||
|
||||
if (lv_iter != values.end()) {
|
||||
switch (lv_iter->lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
row_out.add_value(sr, lv_iter->lv_value.d, ll.is_marked());
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER: {
|
||||
row_out.add_value(sr, lv_iter->lv_value.i, ll.is_marked());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
row_out.sr_details_source_provider = [this](const spectrogram_request& sr,
|
||||
double range_min,
|
||||
double range_max) {
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
auto retval = std::make_unique<filtered_sub_source>();
|
||||
auto begin_line = lss.find_from_time(sr.sr_begin_time).value_or(0_vl);
|
||||
auto end_line = lss.find_from_time(sr.sr_end_time)
|
||||
.value_or(lss.text_line_count());
|
||||
|
||||
retval->fss_delegate = &lss;
|
||||
for (const auto& msg_info : lss.window_at(begin_line, end_line)) {
|
||||
const auto& ll = msg_info.get_logline();
|
||||
if (ll.get_time() >= sr.sr_end_time) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& values = msg_info.get_values();
|
||||
auto lv_iter = find_if(values.begin(),
|
||||
values.end(),
|
||||
logline_value_cmp(&this->lsvs_colname));
|
||||
|
||||
if (lv_iter != values.end()) {
|
||||
switch (lv_iter->lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
if (range_min <= lv_iter->lv_value.d
|
||||
&& lv_iter->lv_value.d < range_max) {
|
||||
retval->fss_lines.emplace_back(
|
||||
msg_info.get_vis_line());
|
||||
}
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER:
|
||||
if (range_min <= lv_iter->lv_value.i
|
||||
&& lv_iter->lv_value.i < range_max) {
|
||||
retval->fss_lines.emplace_back(
|
||||
msg_info.get_vis_line());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
log_spectro_value_source::spectro_mark(textview_curses& tc,
|
||||
time_t begin_time,
|
||||
time_t end_time,
|
||||
double range_min,
|
||||
double range_max)
|
||||
{
|
||||
// XXX need to refactor this and the above method
|
||||
auto& log_tc = lnav_data.ld_views[LNV_LOG];
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
vis_line_t begin_line = lss.find_from_time(begin_time).value_or(0_vl);
|
||||
vis_line_t end_line
|
||||
= lss.find_from_time(end_time).value_or(lss.text_line_count());
|
||||
std::vector<logline_value> values;
|
||||
string_attrs_t sa;
|
||||
|
||||
for (vis_line_t curr_line = begin_line; curr_line < end_line; ++curr_line) {
|
||||
content_line_t cl = lss.at(curr_line);
|
||||
std::shared_ptr<logfile> lf = lss.find(cl);
|
||||
auto ll = lf->begin() + cl;
|
||||
auto format = lf->get_format();
|
||||
shared_buffer_ref sbr;
|
||||
|
||||
if (!ll->is_message()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lf->read_full_message(ll, sbr);
|
||||
sa.clear();
|
||||
values.clear();
|
||||
format->annotate(cl, sbr, sa, values, false);
|
||||
|
||||
auto lv_iter = find_if(values.begin(),
|
||||
values.end(),
|
||||
logline_value_cmp(&this->lsvs_colname));
|
||||
|
||||
if (lv_iter != values.end()) {
|
||||
switch (lv_iter->lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
if (range_min <= lv_iter->lv_value.d
|
||||
&& lv_iter->lv_value.d <= range_max) {
|
||||
log_tc.toggle_user_mark(&textview_curses::BM_USER,
|
||||
curr_line);
|
||||
}
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER:
|
||||
if (range_min <= lv_iter->lv_value.i
|
||||
&& lv_iter->lv_value.i <= range_max) {
|
||||
log_tc.toggle_user_mark(&textview_curses::BM_USER,
|
||||
curr_line);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db_spectro_value_source::db_spectro_value_source(std::string colname)
|
||||
: dsvs_colname(std::move(colname))
|
||||
{
|
||||
this->update_stats();
|
||||
}
|
||||
|
||||
void
|
||||
db_spectro_value_source::update_stats()
|
||||
{
|
||||
this->dsvs_begin_time = 0;
|
||||
this->dsvs_end_time = 0;
|
||||
this->dsvs_stats.clear();
|
||||
|
||||
db_label_source& dls = lnav_data.ld_db_row_source;
|
||||
stacked_bar_chart<std::string>& chart = dls.dls_chart;
|
||||
date_time_scanner dts;
|
||||
|
||||
this->dsvs_column_index = dls.column_name_to_index(this->dsvs_colname);
|
||||
|
||||
if (!dls.has_log_time_column()) {
|
||||
this->dsvs_error_msg
|
||||
= "no 'log_time' column found or not in ascending order, "
|
||||
"unable to create spectrogram";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->dsvs_column_index) {
|
||||
this->dsvs_error_msg = "unknown column -- " + this->dsvs_colname;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dls.dls_headers[this->dsvs_column_index.value()].hm_graphable) {
|
||||
this->dsvs_error_msg = "column is not numeric -- " + this->dsvs_colname;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dls.dls_rows.empty()) {
|
||||
this->dsvs_error_msg = "empty result set";
|
||||
return;
|
||||
}
|
||||
|
||||
stacked_bar_chart<std::string>::bucket_stats_t bs
|
||||
= chart.get_stats_for(this->dsvs_colname);
|
||||
|
||||
this->dsvs_begin_time = dls.dls_time_column.front().tv_sec;
|
||||
this->dsvs_end_time = dls.dls_time_column.back().tv_sec;
|
||||
this->dsvs_stats.lvs_min_value = bs.bs_min_value;
|
||||
this->dsvs_stats.lvs_max_value = bs.bs_max_value;
|
||||
this->dsvs_stats.lvs_count = dls.dls_rows.size();
|
||||
}
|
||||
|
||||
void
|
||||
db_spectro_value_source::spectro_bounds(spectrogram_bounds& sb_out)
|
||||
{
|
||||
auto& dls = lnav_data.ld_db_row_source;
|
||||
|
||||
if (dls.text_line_count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->update_stats();
|
||||
|
||||
sb_out.sb_begin_time = this->dsvs_begin_time;
|
||||
sb_out.sb_end_time = this->dsvs_end_time;
|
||||
sb_out.sb_min_value_out = this->dsvs_stats.lvs_min_value;
|
||||
sb_out.sb_max_value_out = this->dsvs_stats.lvs_max_value;
|
||||
sb_out.sb_count = this->dsvs_stats.lvs_count;
|
||||
}
|
||||
|
||||
void
|
||||
db_spectro_value_source::spectro_row(spectrogram_request& sr,
|
||||
spectrogram_row& row_out)
|
||||
{
|
||||
auto& dls = lnav_data.ld_db_row_source;
|
||||
auto begin_row = dls.row_for_time({sr.sr_begin_time, 0}).value_or(0_vl);
|
||||
auto end_row
|
||||
= dls.row_for_time({sr.sr_end_time, 0}).value_or(dls.dls_rows.size());
|
||||
|
||||
for (auto lpc = begin_row; lpc < end_row; ++lpc) {
|
||||
double value = 0.0;
|
||||
|
||||
sscanf(
|
||||
dls.dls_rows[lpc][this->dsvs_column_index.value()], "%lf", &value);
|
||||
|
||||
row_out.add_value(sr, value, false);
|
||||
}
|
||||
|
||||
row_out.sr_details_source_provider = [this](const spectrogram_request& sr,
|
||||
double range_min,
|
||||
double range_max) {
|
||||
auto& dls = lnav_data.ld_db_row_source;
|
||||
auto retval = std::make_unique<filtered_sub_source>();
|
||||
|
||||
retval->fss_delegate = &dls;
|
||||
auto begin_row = dls.row_for_time({sr.sr_begin_time, 0}).value_or(0_vl);
|
||||
auto end_row = dls.row_for_time({sr.sr_end_time, 0})
|
||||
.value_or(dls.dls_rows.size());
|
||||
|
||||
for (auto lpc = begin_row; lpc < end_row; ++lpc) {
|
||||
double value = 0.0;
|
||||
|
||||
sscanf(dls.dls_rows[lpc][this->dsvs_column_index.value()],
|
||||
"%lf",
|
||||
&value);
|
||||
if (range_min <= value && value < range_max) {
|
||||
retval->fss_lines.emplace_back(lpc);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2007-2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lnav_spectro_impls_hh
|
||||
#define lnav_spectro_impls_hh
|
||||
|
||||
#include "log_format.hh"
|
||||
#include "spectro_source.hh"
|
||||
|
||||
class log_spectro_value_source : public spectrogram_value_source {
|
||||
public:
|
||||
log_spectro_value_source(intern_string_t colname);
|
||||
|
||||
void update_stats();
|
||||
|
||||
void spectro_bounds(spectrogram_bounds& sb_out) override;
|
||||
|
||||
void spectro_row(spectrogram_request& sr,
|
||||
spectrogram_row& row_out) override;
|
||||
|
||||
void spectro_mark(textview_curses& tc,
|
||||
time_t begin_time,
|
||||
time_t end_time,
|
||||
double range_min,
|
||||
double range_max) override;
|
||||
|
||||
intern_string_t lsvs_colname;
|
||||
logline_value_stats lsvs_stats;
|
||||
time_t lsvs_begin_time{0};
|
||||
time_t lsvs_end_time{0};
|
||||
bool lsvs_found{false};
|
||||
};
|
||||
|
||||
class db_spectro_value_source : public spectrogram_value_source {
|
||||
public:
|
||||
db_spectro_value_source(std::string colname);
|
||||
|
||||
void update_stats();
|
||||
|
||||
void spectro_bounds(spectrogram_bounds& sb_out) override;
|
||||
|
||||
void spectro_row(spectrogram_request& sr,
|
||||
spectrogram_row& row_out) override;
|
||||
|
||||
void spectro_mark(textview_curses& tc,
|
||||
time_t begin_time,
|
||||
time_t end_time,
|
||||
double range_min,
|
||||
double range_max) override
|
||||
{
|
||||
}
|
||||
|
||||
std::string dsvs_colname;
|
||||
logline_value_stats dsvs_stats;
|
||||
time_t dsvs_begin_time{0};
|
||||
time_t dsvs_end_time{0};
|
||||
nonstd::optional<size_t> dsvs_column_index;
|
||||
std::string dsvs_error_msg;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue