Расширить DSView осциллографом и декодерами двигателя и сигма-дельта
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
--- a/DSView/pv/view/view.h
|
||||
+++ b/DSView/pv/view/view.h
|
||||
@@ -64,4 +64,5 @@
|
||||
namespace view {
|
||||
|
||||
+class ScopeTrace; // Виртуальные аналоговые дорожки принадлежат представлению и не меняют аппаратный захват.
|
||||
class Header;
|
||||
class DevMode;
|
||||
@@ -439,4 +440,6 @@
|
||||
pv::toolbars::SamplingBar *_sampling_bar;
|
||||
|
||||
+ std::vector<ScopeTrace*> _scope_traces; // Четыре демонстрационных канала сохраняют настройки видимости в текущей сессии.
|
||||
+ QAction *_scope_action = nullptr; // Доступ к меню зависит от цифрового режима текущего прибора.
|
||||
QWidget *_viewcenter;
|
||||
ViewStatus *_viewbottom;
|
||||
--- a/DSView/pv/view/view.cpp
|
||||
+++ b/DSView/pv/view/view.cpp
|
||||
@@ -42,4 +42,7 @@
|
||||
#include "lissajoustrace.h"
|
||||
#include "analogsignal.h"
|
||||
+#include "scope_trace.h" // Аналоговая эмуляция Zynq отображается вместе с цифровыми каналами.
|
||||
+#include <QToolButton> // Меню каналов встраивается в существующую прокручиваемую панель управления.
|
||||
+#include <QMenu> // Флажки позволяют независимо добавить и убрать каждый канал осциллографа.
|
||||
|
||||
#include "../sigsession.h"
|
||||
@@ -202,4 +205,28 @@
|
||||
connect(_header, SIGNAL(header_updated()),this, SLOT(header_updated()));
|
||||
|
||||
+ _trace_view_map[ScopeTrace::Type] = TIME_VIEW; // Виртуальный осциллограф использует ту же шкалу времени и прокрутку, что цифровые сигналы.
|
||||
+ auto scope_button = new QToolButton(_sampling_bar); // Кнопка принадлежит штатной панели DSView и работает также без SETGUI.
|
||||
+ scope_button->setObjectName("scope_channel_button"); // Постоянное имя позволяет проверять настоящий интерфейс в нативном тесте.
|
||||
+ scope_button->setText(QString::fromUtf8("Осциллограф +")); // Название явно обозначает добавление аналоговой дорожки к существующим сигналам.
|
||||
+ scope_button->setToolTip(QString::fromUtf8("Добавить канал осциллографа: эмуляция Zynq на общей шкале времени")); // Пользователь видит происхождение данных до включения канала.
|
||||
+ scope_button->setPopupMode(QToolButton::InstantPopup); // Одно нажатие сразу открывает выбор каналов.
|
||||
+ auto scope_menu = new QMenu(scope_button); // Меню и его действия удаляются вместе с панелью управления.
|
||||
+ scope_button->setMenu(scope_menu); // Выбор канала выполняется внутри существующего DSView без отдельного окна генератора.
|
||||
+ for (unsigned channel = 0; channel < 4; ++channel) { // Эмулятор Zynq предоставляет четыре независимых канала CH1…CH4.
|
||||
+ auto trace = new ScopeTrace(channel); // Сохраняем точные частоты, формы и амплитуды исходного эмулятора.
|
||||
+ trace->setParent(this); // QObject освобождает дорожку при уничтожении представления без участия аппаратной сессии.
|
||||
+ _scope_traces.push_back(trace); // Постоянный объект сохраняет имя, цвет и высоту при скрытии и повторном включении.
|
||||
+ auto action = scope_menu->addAction(QString::fromUtf8("CH%1 · %2 · %3 Гц · ±%4 В").arg(channel + 1).arg((channel & 1) ? QString::fromUtf8("меандр") : QString::fromUtf8("синус")).arg(set_scope::hz[channel]).arg(set_scope::peak[channel])); // Подпись показывает параметры до добавления канала.
|
||||
+ action->setObjectName(QString("scope_channel_%1").arg(channel + 1)); // Идентификатор связывает действие с конкретным каналом при проверке интерфейса.
|
||||
+ action->setCheckable(true); // Флажок одновременно показывает состояние и позволяет убрать дорожку повторным нажатием.
|
||||
+ connect(action, &QAction::toggled, this, [this, trace](bool active) { // Меняем только выбранный аналоговый канал, сохраняя цифровые сигналы и декодеры.
|
||||
+ trace->set_active(active); // Источник не запускает отдельный захват и не открывает сетевое соединение.
|
||||
+ signals_changed(nullptr); // Пересчитываем высоту дорожек и диапазон общей вертикальной прокрутки.
|
||||
+ set_all_update(true); // Обновляем кэш диаграммы после изменения набора дорожек.
|
||||
+ viewport_update(); // Новый канал появляется сразу, в том числе до первого аппаратного захвата.
|
||||
+ }); // Контекст View отключает обработчик при закрытии его окна.
|
||||
+ } // Каждый канал можно включать независимо от остальных трёх.
|
||||
+ _scope_action = _sampling_bar->addWidget(scope_button); // Кнопка доступна рядом с обычными настройками захвата, включая отдельный запуск DSView.
|
||||
+ _scope_action->setEnabled(_device_agent->have_instance() && _device_agent->get_work_mode() == LOGIC); // Добавление доступно для цифровых источников, без вмешательства в штатный DSO.
|
||||
ADD_UI(this);
|
||||
}
|
||||
@@ -393,4 +420,8 @@
|
||||
traces.push_back(t);
|
||||
}
|
||||
+
|
||||
+ if (_device_agent->get_work_mode() == LOGIC && (type == ALL_VIEW || type == TIME_VIEW)) { // Виртуальные аналоги дополняют только цифровую временную область.
|
||||
+ for (auto trace : _scope_traces) if (trace->enabled()) traces.push_back(trace); // Скрытые каналы не занимают место и не участвуют в обработке мыши.
|
||||
+ } // Данные устройства, спектр и аппаратный осциллограф сохраняют собственные списки каналов.
|
||||
|
||||
auto lissajous = _session->get_lissajous_trace();
|
||||
@@ -706,4 +737,5 @@
|
||||
void View::signals_changed(const Trace* eventTrace)
|
||||
{
|
||||
+ if (_scope_action) _scope_action->setEnabled(_device_agent->have_instance() && _device_agent->get_work_mode() == LOGIC); // Меню следует за сменой прибора или режима без удаления выбранных демо-каналов.
|
||||
double actualMargin = SignalMargin;
|
||||
int total_rows = 0;
|
||||
@@ -841,4 +873,9 @@
|
||||
time_traces.push_back(t);
|
||||
}
|
||||
+
|
||||
+ for (auto trace : _scope_traces) { // Аналоговые дорожки участвуют в общей сортировке после перемещения мышью.
|
||||
+ if (!trace->enabled()) continue; // Скрытый демо-канал не занимает места в компоновке.
|
||||
+ if (trace->get_view_index() == -1) time_traces.push_back(trace); else all_traces.push_back(trace); // Новый канал появляется сверху, перемещённый сохраняет выбранный порядок среди цифровых сигналов.
|
||||
+ } // Переупорядочивание не требует добавлять эмуляцию в аппаратный список каналов.
|
||||
|
||||
sort(all_traces.begin(), all_traces.end(), compare_trace_view_index);
|
||||
--- a/DSView/pv/mainwindow.cpp
|
||||
+++ b/DSView/pv/mainwindow.cpp
|
||||
@@ -612,4 +612,38 @@
|
||||
}
|
||||
|
||||
+ void MainWindow::checkHostScope() // Проверяет добавление аналоговых дорожек отдельно от сценариев диалогов декодеров.
|
||||
+ { // Отдельная точка запуска не требует Ethernet-патча и доступна в базовой сборке DSView.
|
||||
+ if (qEnvironmentVariableIsSet("DSVIEW_HOST_CHECK_SCOPE")) { // Явный проверочный режим использует реальные действия меню, не подключая USB или Ethernet.
|
||||
+ const auto original_signals = _session->get_signals(); // Копия указателей проверяет сохранность существующей цифровой записи.
|
||||
+ std::vector<pv::view::Trace*> before; // Список позволяет обнаружить потерю или лишнее добавление любой дорожки.
|
||||
+ _view->get_traces(ALL_VIEW, before); // Исходное число включает цифровые сигналы и уже добавленные декодеры.
|
||||
+ const double scale = _view->scale(); // Включение осциллографа обязано сохранить выбранный масштаб времени.
|
||||
+ QList<QAction*> channels; // Используем те же QAction, которые выбирает пользователь в меню осциллографа.
|
||||
+ bool passed = true; // Любое несоответствие делает нативный сценарий неуспешным.
|
||||
+ for (int channel = 1; channel <= 4; ++channel) { // Проверяем независимое включение всех четырёх источников Zynq.
|
||||
+ auto action = _sampling_bar->findChild<QAction*>(QString("scope_channel_%1").arg(channel)); // Поиск идёт только в панели текущего экземпляра DSView.
|
||||
+ if (!action) { passed = false; break; } // Отсутствие пользовательского элемента управления не маскируется проверкой только модели.
|
||||
+ channels.append(action); // Сохраняем реальные действия для проверки удаления и повторного добавления.
|
||||
+ action->setChecked(true); // Сигнал toggled выполняет производственный путь компоновки и обновления графика.
|
||||
+ std::vector<pv::view::Trace*> current; // Новый список должен содержать ровно одну дополнительную дорожку на выбранный канал.
|
||||
+ _view->get_traces(ALL_VIEW, current); // Проверяем состояние того же списка, который рисует Viewport.
|
||||
+ passed = passed && current.size() == before.size() + channel && _view->scale() == scale; // Добавление не должно терять цифровые каналы или сбрасывать время.
|
||||
+ } // Все четыре канала одновременно участвуют в итоговой компоновке.
|
||||
+ for (auto action : channels) action->setChecked(false); // Убираем все добавленные дорожки через пользовательские действия.
|
||||
+ std::vector<pv::view::Trace*> after; // Повторное чтение обнаруживает оставшиеся скрытые трассы в активном списке.
|
||||
+ _view->get_traces(ALL_VIEW, after); // Исходная сессия должна восстановиться без перезагрузки захвата.
|
||||
+ passed = passed && after == before && _session->get_signals() == original_signals; // Сравниваем идентичность объектов, а не только количество каналов.
|
||||
+ if (channels.size() == 4) { channels[0]->setChecked(true); channels[1]->setChecked(true); } // Итоговый снимок показывает синус и меандр рядом с исходными цифровыми сигналами.
|
||||
+ _view->set_scale_offset(2e-6, 0); // Две микросекунды на пиксель позволяют визуально проверить форму синуса 1 кГц и меандра 500,3 Гц.
|
||||
+ QTimer::singleShot(200, this, [this, passed]() { // Даём нативному циклу событий завершить компоновку и перерисовку всех дорожек.
|
||||
+ const QString screenshot = QString::fromUtf8(qgetenv("DSVIEW_HOST_SCREENSHOT")); // Артефакт сохраняется только по явно указанному тестом пути.
|
||||
+ const bool rendered = _view->grab().save(screenshot); // Проверяем фактический рендер настоящего Qt-представления, включая цифровые каналы.
|
||||
+ QFile report(QString::fromUtf8(qgetenv("DSVIEW_HOST_CHECK"))); // Тест получает результат через существующий диагностический канал DSView.
|
||||
+ if (report.open(QIODevice::WriteOnly | QIODevice::Truncate)) report.write(passed && rendered ? "PASS scope add/remove/digital preservation/render\n" : "FAIL scope integration\n"); // Успех требует корректного поведения меню и сохранённого изображения.
|
||||
+ }); // Захват и дочерний процесс продолжают работать до штатной команды CLOSE от теста.
|
||||
+ return; // Проверка осциллографа не запускает посторонние сценарии диалогов декодеров.
|
||||
+ } // В обычном запуске эта ветка полностью отключена.
|
||||
+ } // В обычном запуске проверочная функция не вызывается.
|
||||
+
|
||||
void MainWindow::on_protocol(bool visible)
|
||||
{
|
||||
--- a/DSView/pv/view/header.cpp
|
||||
+++ b/DSView/pv/view/header.cpp
|
||||
@@ -316,11 +316,5 @@
|
||||
std::vector<Trace*> traces;
|
||||
|
||||
- for (auto s : _view.session().get_decode_signals()){
|
||||
- traces.push_back(s);
|
||||
- }
|
||||
-
|
||||
- for (auto s : _view.session().get_signals()){
|
||||
- traces.push_back(s);
|
||||
- }
|
||||
+ _view.get_traces(TIME_VIEW, traces); // Индексы перемещения включают также виртуальные аналоговые дорожки, отсутствующие в аппаратной сессии.
|
||||
|
||||
sort(traces.begin(), traces.end(), View::compare_trace_y);
|
||||
--- a/DSView/pv/mainwindow.h
|
||||
+++ b/DSView/pv/mainwindow.h
|
||||
@@ -98,4 +98,5 @@
|
||||
const char *openHostFile(const QString &path);
|
||||
void checkHostDecoders();
|
||||
+ void checkHostScope(); // Нативный сценарий проверяет реальные действия меню и сохранность цифровой записи.
|
||||
|
||||
public slots:
|
||||
--- a/DSView/pv/mainframe.cpp
|
||||
+++ b/DSView/pv/mainframe.cpp
|
||||
@@ -769,5 +769,5 @@
|
||||
QFrame::show();
|
||||
if (!qgetenv("DSVIEW_HOST_CHECK").isEmpty())
|
||||
- QTimer::singleShot(3500, _mainWindow, &MainWindow::checkHostDecoders);
|
||||
+ QTimer::singleShot(3500, _mainWindow, qEnvironmentVariableIsSet("DSVIEW_HOST_CHECK_SCOPE") ? &MainWindow::checkHostScope : &MainWindow::checkHostDecoders); // Явный параметр выбирает проверку осциллографа, сохраняя прежние сценарии декодеров.
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user