Add new debug tools to FSComm to help on development. Check tools->debug.

This commit is contained in:
Joao Mesquita 2010-04-07 02:05:03 -03:00
parent d2ca27259a
commit ec8ab06c66
14 changed files with 1177 additions and 6 deletions

View File

@ -33,7 +33,10 @@ SOURCES += main.cpp \
preferences/prefaccounts.cpp \ preferences/prefaccounts.cpp \
account.cpp \ account.cpp \
widgets/codecwidget.cpp \ widgets/codecwidget.cpp \
channel.cpp channel.cpp \
debugtools/consolewindow.cpp \
debugtools/sortfilterproxymodel.cpp \
debugtools/statedebugdialog.cpp
HEADERS += mainwindow.h \ HEADERS += mainwindow.h \
fshost.h \ fshost.h \
call.h \ call.h \
@ -45,10 +48,15 @@ HEADERS += mainwindow.h \
preferences/prefaccounts.h \ preferences/prefaccounts.h \
account.h \ account.h \
widgets/codecwidget.h \ widgets/codecwidget.h \
channel.h channel.h \
debugtools/consolewindow.h \
debugtools/sortfilterproxymodel.h \
debugtools/statedebugdialog.h
FORMS += mainwindow.ui \ FORMS += mainwindow.ui \
preferences/prefdialog.ui \ preferences/prefdialog.ui \
preferences/accountdialog.ui \ preferences/accountdialog.ui \
widgets/codecwidget.ui widgets/codecwidget.ui \
debugtools/consolewindow.ui \
debugtools/statedebugdialog.ui
RESOURCES += resources.qrc RESOURCES += resources.qrc
OTHER_FILES += conf/freeswitch.xml OTHER_FILES += conf/freeswitch.xml

View File

@ -0,0 +1,158 @@
#include "consolewindow.h"
#include "ui_consolewindow.h"
ConsoleWindow::ConsoleWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ConsoleWindow),
findNext(false)
{
ui->setupUi(this);
sourceModel = new ConsoleModel(this);
model = new SortFilterProxyModel(this);
model->setSourceModel(sourceModel);
model->setFilterKeyColumn(0);
ui->consoleListView->setModel(model);
ui->consoleListView->setColumnWidth(0, 2000);
connect(sourceModel, SIGNAL(beforeInserting()),
this, SLOT(setConditionalScroll()));
connect(sourceModel, SIGNAL(afterInserting()),
this, SLOT(conditionalScroll()));
connect(ui->btnSend, SIGNAL(clicked()),
this, SLOT(cmdSendClicked()));
connect(ui->lineCmd, SIGNAL(textChanged(QString)),
this, SLOT(lineCmdChanged(QString)));
_levelFilter = new QSignalMapper(this);
connect(ui->checkEmerg, SIGNAL(clicked()), _levelFilter, SLOT(map()));
connect(ui->checkAlert, SIGNAL(clicked()), _levelFilter, SLOT(map()));
connect(ui->checkCrit, SIGNAL(clicked()), _levelFilter, SLOT(map()));
connect(ui->checkDebug, SIGNAL(clicked()), _levelFilter, SLOT(map()));
connect(ui->checkError, SIGNAL(clicked()), _levelFilter, SLOT(map()));
connect(ui->checkInfo, SIGNAL(clicked()), _levelFilter, SLOT(map()));
connect(ui->checkNotice, SIGNAL(clicked()), _levelFilter, SLOT(map()));
connect(ui->checkWarn, SIGNAL(clicked()), _levelFilter, SLOT(map()));
_levelFilter->setMapping(ui->checkEmerg, SWITCH_LOG_CONSOLE);
_levelFilter->setMapping(ui->checkAlert, SWITCH_LOG_ALERT);
_levelFilter->setMapping(ui->checkCrit, SWITCH_LOG_CRIT);
_levelFilter->setMapping(ui->checkDebug, SWITCH_LOG_DEBUG);
_levelFilter->setMapping(ui->checkError, SWITCH_LOG_ERROR);
_levelFilter->setMapping(ui->checkInfo, SWITCH_LOG_INFO);
_levelFilter->setMapping(ui->checkNotice, SWITCH_LOG_NOTICE);
_levelFilter->setMapping(ui->checkWarn, SWITCH_LOG_WARNING);
connect(_levelFilter, SIGNAL(mapped(int)), this, SLOT(filterModelLogLevel(int)));
connect(ui->btnFilterClear, SIGNAL(clicked()),
this, SLOT(filterClear()));
connect(ui->lineFilter, SIGNAL(textChanged(QString)),
this, SLOT(filterStringChanged()));
connect(ui->filterCaseSensitivityCheckBox, SIGNAL(toggled(bool)),
this, SLOT(filterStringChanged()));
connect(ui->filterSyntaxComboBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(filterStringChanged()));
connect(ui->filterReverseCheckBox, SIGNAL(toggled(bool)),
this, SLOT(reverseFilterChecked()));
connect(&g_FSHost, SIGNAL(eventLog(QSharedPointer<switch_log_node_t>,switch_log_level_t)), this, SLOT(loggerHandler(QSharedPointer<switch_log_node_t>,switch_log_level_t)));
}
ConsoleWindow::~ConsoleWindow()
{
delete ui;
}
void ConsoleWindow::changeEvent(QEvent *e)
{
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
void ConsoleWindow::setConditionalScroll()
{
autoScroll = (ui->consoleListView->verticalScrollBar()->maximum() == ui->consoleListView->verticalScrollBar()->value());
}
void ConsoleWindow::conditionalScroll()
{
if (autoScroll)
ui->consoleListView->scrollToBottom();
}
void ConsoleWindow::cmdSendClicked()
{
if (ui->lineCmd->text().isEmpty()) return;
QString cmd = ui->lineCmd->text().split(" ", QString::SkipEmptyParts)[0];
if (cmd.isEmpty()) return;
QStringList split = ui->lineCmd->text().split(" ", QString::SkipEmptyParts);
if (split.isEmpty()) return;
QString args;
for (int i=1; i<split.length(); i++)
{
args += split[i];
if (i!=split.length()-1)
args += " ";
}
QString res;
g_FSHost.sendCmd(cmd.toAscii().data(), args.toAscii().data(), &res);
QStandardItem *item = new QStandardItem(res);
item->setData(SWITCH_LOG_CONSOLE, ConsoleModel::LogLevelRole);
addNewConsoleItem(item);
ui->lineCmd->clear();
}
void ConsoleWindow::lineCmdChanged(QString text)
{
ui->btnSend->setDisabled(text.isEmpty());
}
void ConsoleWindow::filterModelLogLevel(int level)
{
model->setLogLevelFilter(level);
}
void ConsoleWindow::loggerHandler(QSharedPointer<switch_log_node_t> node, switch_log_level_t level)
{
if (level > ui->comboLogLevel->currentIndex()) return;
QString text(node.data()->data);
if (!text.isEmpty())
{
/* Remove \r\n */
QStringList textList = text.split(QRegExp("(\r+)"), QString::SkipEmptyParts);
QString final_str;
for (int line = 0; line<textList.size(); line++)
{
final_str += textList[line];
}
QStringList lines = final_str.split(QRegExp("(\n+)"), QString::SkipEmptyParts);
for (int line = 0; line < lines.size(); ++line)
{
QStandardItem *item = new QStandardItem(lines[line]);
item->setData(level, ConsoleModel::LogLevelRole);
item->setData(node.data()->userdata, ConsoleModel::UUIDRole);
addNewConsoleItem(item);
}
}
}
void ConsoleWindow::addNewConsoleItem(QStandardItem *item)
{
QSettings settings;
settings.beginGroup("Console");
QPalette palette = settings.value(QString("log-level-%1-palette").arg(item->data(Qt::UserRole).toInt())).value<QPalette>();
QFont font = settings.value(QString("log-level-%1-font").arg(item->data(Qt::UserRole).toInt())).value<QFont>();
item->setBackground(palette.base());
item->setForeground(palette.text());
item->setFont(font);
sourceModel->appendRow(item);
}

View File

@ -0,0 +1,56 @@
#ifndef CONSOLEWINDOW_H
#define CONSOLEWINDOW_H
#include <QtGui>
#include "fshost.h"
#include "sortfilterproxymodel.h"
namespace Ui {
class ConsoleWindow;
}
class ConsoleWindow : public QMainWindow {
Q_OBJECT
public:
ConsoleWindow(QWidget *parent = 0);
~ConsoleWindow();
protected:
void changeEvent(QEvent *e);
/*public slots:
void clearConsoleContents();
void saveLogToFile();
void pastebinLog();
void filterLogUUID(QString);
void findText();*/
private slots:
void setConditionalScroll();
void conditionalScroll();
/*void filterClear();
void filterStringChanged();*/
void loggerHandler(QSharedPointer<switch_log_node_t> node, switch_log_level_t level);
void addNewConsoleItem(QStandardItem *item);
void cmdSendClicked();
void lineCmdChanged(QString);
/*void reverseFilterChecked();*/
void filterModelLogLevel(int);
private:
Ui::ConsoleWindow *ui;
ConsoleModel *sourceModel;
QModelIndexList foundItems;
SortFilterProxyModel *model;
/*pastebinDialog *_pastebinDlg;
FindDialog *_findDialog;*/
bool findNext;
bool autoScroll;
QSignalMapper *_levelFilter;
/*void readSettings();
void writeSettings();*/
};
#endif // CONSOLEWINDOW_H

View File

@ -0,0 +1,466 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConsoleWindow</class>
<widget class="QMainWindow" name="ConsoleWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>696</width>
<height>559</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>118</height>
</size>
</property>
<property name="title">
<string>Filter</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="lineFilter">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="filterSyntaxComboBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContentsOnFirstShow</enum>
</property>
<item>
<property name="text">
<string>Regular Expression</string>
</property>
</item>
<item>
<property name="text">
<string>Wildcard</string>
</property>
</item>
<item>
<property name="text">
<string>Fixed String</string>
</property>
</item>
<item>
<property name="text">
<string>UUID</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnFilterClear">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Clear</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="filterCaseSensitivityCheckBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Case sensitive filter</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="filterReverseCheckBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Reverse filter</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblWarningMsg">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>358</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget_2">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="title">
<string>Log Level Filter</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="checkEmerg">
<property name="text">
<string>Console</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkAlert">
<property name="text">
<string>Alert</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkCrit">
<property name="text">
<string>Critical</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkError">
<property name="text">
<string>Error</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkWarn">
<property name="text">
<string>Warning</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkNotice">
<property name="text">
<string>Notice</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkInfo">
<property name="text">
<string>Info</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkDebug">
<property name="text">
<string>Debug</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>18</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="title">
<string>Loglevel</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QComboBox" name="comboLogLevel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="currentIndex">
<number>7</number>
</property>
<item>
<property name="text">
<string>Console</string>
</property>
</item>
<item>
<property name="text">
<string>Alert</string>
</property>
</item>
<item>
<property name="text">
<string>Critical</string>
</property>
</item>
<item>
<property name="text">
<string>Error</string>
</property>
</item>
<item>
<property name="text">
<string>Warning</string>
</property>
</item>
<item>
<property name="text">
<string>Notice</string>
</property>
</item>
<item>
<property name="text">
<string>Info</string>
</property>
</item>
<item>
<property name="text">
<string>Debug</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QTableView" name="consoleListView">
<property name="autoScroll">
<bool>false</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="tabKeyNavigation">
<bool>false</bool>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>20</number>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineCmd">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>26</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSend">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>28</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>85</width>
<height>28</height>
</size>
</property>
<property name="text">
<string>Send</string>
</property>
<property name="shortcut">
<string>Return</string>
</property>
<property name="autoDefault">
<bool>true</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>696</width>
<height>24</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,116 @@
#include <QtGui>
#include "sortfilterproxymodel.h"
ConsoleModel::ConsoleModel (QObject *parent)
: QAbstractTableModel(parent)
{
QSettings settings;
batchSize = settings.value("Console/batchSize", 200).toInt();
insertionTimer = new QBasicTimer;
insertionTimer->start(0, this);
}
int ConsoleModel::rowCount ( const QModelIndex & parent ) const
{
if (parent.isValid())
return 0;
return _listDisplayModel.count();
}
int ConsoleModel::columnCount ( const QModelIndex & /*parent*/ ) const
{
return 1;
}
QVariant ConsoleModel::data ( const QModelIndex & index, int role ) const
{
if (!index.isValid())
return QVariant();
return _listDisplayModel.at(index.row())->data(role);
}
void ConsoleModel::clear()
{
_listDisplayModel.clear();
reset();
}
void ConsoleModel::appendRow ( QStandardItem* item )
{
_listInsertModel.append(item);
insertionTimer->start(0, this);
}
void ConsoleModel::timerEvent(QTimerEvent *e)
{
if (e->timerId() == insertionTimer->timerId())
{
if (!_listInsertModel.isEmpty())
{
int inserted_items = 0;
int toBeInserted = 0;
if (_listInsertModel.size() < batchSize)
{
toBeInserted = _listInsertModel.size() - 1;
} else {
toBeInserted = batchSize - 1;
}
emit beforeInserting();
beginInsertRows( QModelIndex(), _listDisplayModel.size(), _listDisplayModel.size() + toBeInserted );
while( !_listInsertModel.isEmpty() && inserted_items <= batchSize)
{
_listDisplayModel.append(_listInsertModel.takeFirst());
inserted_items++;
}
endInsertRows();
emit afterInserting();
} else {
insertionTimer->stop();
}
}
}
SortFilterProxyModel::SortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
reverseFlag = false;
for(int i = 0; i < 8; i++)
loglevels.insert(i, true);
}
void SortFilterProxyModel::toggleReverseFlag()
{
reverseFlag = !reverseFlag;
invalidateFilter();
}
void SortFilterProxyModel::setLogLevelFilter(int level)
{
loglevels.replace(level, loglevels.value(level) == false);
// Let us filter
invalidateFilter();
}
void SortFilterProxyModel::setUUIDFilterLog(QString uuid)
{
_uuid = uuid;
invalidateFilter();
}
bool SortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
QModelIndex index0 = sourceModel()->index(source_row, 0, source_parent);
bool uuidMatch = true;
if (!_uuid.isEmpty())
uuidMatch = (sourceModel()->data(index0, ConsoleModel::UUIDRole).toString() == _uuid);
bool res = (loglevels.value(sourceModel()->data(index0, Qt::UserRole).toInt()) == true
&& sourceModel()->data(index0).toString().contains(filterRegExp())
&& uuidMatch);
if (reverseFlag)
return !res;
else
return res;
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2007, Anthony Minessale II
* 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 the original author; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER
* 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.
*
* Contributor(s):
*
* Joao Mesquita <jmesquita (at) freeswitch.org>
*
*/
#ifndef SORTFILTERPROXYMODEL_H
#define SORTFILTERPROXYMODEL_H
#include <QSortFilterProxyModel>
#include <QAbstractTableModel>
#include <QVector>
#include <QList>
class QBasicTimer;
class QStandardItem;
class QScrollBar;
class ConsoleModel : public QAbstractTableModel
{
Q_OBJECT
public:
ConsoleModel (QObject *parent = 0);
int rowCount ( const QModelIndex & parent = QModelIndex() ) const;
int columnCount ( const QModelIndex & parent = QModelIndex() ) const;
QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
void appendRow ( QStandardItem* item );
void clear();
QList<QStandardItem *> modelData() { return _listDisplayModel; }
enum {
LogLevelRole = Qt::UserRole,
UUIDRole
};
signals:
void beforeInserting();
void afterInserting();
protected:
void timerEvent(QTimerEvent *);
private:
QList<QStandardItem *> _listDisplayModel;
QList<QStandardItem *> _listInsertModel;
int batchSize;
QBasicTimer *insertionTimer;
};
class SortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
SortFilterProxyModel(QObject *parent = 0);
void setLogLevelFilter(int level);
void setUUIDFilterLog(QString uuid);
void toggleReverseFlag();
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
private:
QVector<bool> loglevels;
QString _uuid;
bool reverseFlag;
};
#endif // SORTFILTERPROXYMODEL_H

View File

@ -0,0 +1,76 @@
#include "statedebugdialog.h"
#include "ui_statedebugdialog.h"
StateDebugDialog::StateDebugDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::StateDebugDialog)
{
ui->setupUi(this);
connect(&g_FSHost, SIGNAL(newEvent(QSharedPointer<switch_event_t>)), this, SLOT(newEvent(QSharedPointer<switch_event_t>)));
connect(ui->listUUID, SIGNAL(itemSelectionChanged()), this, SLOT(currentUuidChanged()));
connect(ui->listEvents, SIGNAL(itemSelectionChanged()), this, SLOT(currentEventsChanged()));
}
StateDebugDialog::~StateDebugDialog()
{
delete ui;
}
void StateDebugDialog::changeEvent(QEvent *e)
{
QDialog::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
void StateDebugDialog::newEvent(QSharedPointer<switch_event_t>event)
{
/* We don't want to keep track of events that are not calls at this moment */
if (QString(switch_event_get_header_nil(event.data(), "Unique-ID")).isEmpty())
return;
QString uuid(switch_event_get_header_nil(event.data(), "Unique-ID"));
if (!_events.contains(uuid))
{
QList<QSharedPointer<switch_event_t> > tmpListEvents;
tmpListEvents.append(event);
_events.insert(uuid, tmpListEvents);
ui->listUUID->addItem(new QListWidgetItem(uuid));
}
else
{
QList<QSharedPointer<switch_event_t> > tmpListEvents = _events.value(uuid);
tmpListEvents.append(event);
_events.insert(uuid, tmpListEvents);
}
}
void StateDebugDialog::currentUuidChanged()
{;
ui->listEvents->clear();
ui->listDetails->clear();
QString uuid = ui->listUUID->currentItem()->text();
foreach(QSharedPointer<switch_event_t> e, _events.value(uuid))
{
ui->listEvents->addItem(new QListWidgetItem(switch_event_name(e.data()->event_id)));
}
}
void StateDebugDialog::currentEventsChanged()
{
ui->listDetails->clear();
int r = ui->listEvents->currentRow();
QString uuid = ui->listUUID->currentItem()->text();
QList<QSharedPointer<switch_event_t> > tmpListEvents = _events.value(uuid);
QSharedPointer<switch_event_t> e = tmpListEvents.at(r);
for(switch_event_header_t* h = e.data()->headers; h != e.data()->last_header; h = h->next)
{
ui->listDetails->addItem(new QListWidgetItem(QString("%1 = %2").arg(h->name, h->value)));
}
}

View File

@ -0,0 +1,30 @@
#ifndef STATEDEBUGDIALOG_H
#define STATEDEBUGDIALOG_H
#include <QtGui>
#include "fshost.h"
namespace Ui {
class StateDebugDialog;
}
class StateDebugDialog : public QDialog {
Q_OBJECT
public:
StateDebugDialog(QWidget *parent = 0);
~StateDebugDialog();
private slots:
void newEvent(QSharedPointer<switch_event_t> event);
void currentUuidChanged();
void currentEventsChanged();
protected:
void changeEvent(QEvent *e);
private:
Ui::StateDebugDialog *ui;
QHash<QString, QList<QSharedPointer<switch_event_t> > > _events;
};
#endif // STATEDEBUGDIALOG_H

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StateDebugDialog</class>
<widget class="QDialog" name="StateDebugDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>804</width>
<height>235</height>
</rect>
</property>
<property name="windowTitle">
<string>Debug Events</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>UUID</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listUUID"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>EVENTS</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listEvents"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>DETAILS</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listDetails"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -43,7 +43,10 @@ FSHost::FSHost(QObject *parent) :
switch_core_set_globals(); switch_core_set_globals();
qRegisterMetaType<QSharedPointer<Call> >("QSharedPointer<Call>"); qRegisterMetaType<QSharedPointer<Call> >("QSharedPointer<Call>");
qRegisterMetaType<QSharedPointer<Call> >("QSharedPointer<Channel>"); qRegisterMetaType<QSharedPointer<switch_event_t> >("QSharedPointer<switch_event_t>");
qRegisterMetaType<QSharedPointer<switch_log_node_t> >("QSharedPointer<switch_log_node_t>");
qRegisterMetaType<switch_log_level_t>("switch_log_level_t");
qRegisterMetaType<QSharedPointer<Channel> >("QSharedPointer<Channel>");
qRegisterMetaType<QSharedPointer<Account> >("QSharedPointer<Account>"); qRegisterMetaType<QSharedPointer<Account> >("QSharedPointer<Account>");
connect(this, SIGNAL(loadedModule(QString,QString)), this, SLOT(minimalModuleLoaded(QString,QString))); connect(this, SIGNAL(loadedModule(QString,QString)), this, SLOT(minimalModuleLoaded(QString,QString)));
@ -115,6 +118,11 @@ void FSHost::createFolders()
} }
} }
void FSHost::generalLoggerHandler(QSharedPointer<switch_log_node_t>node, switch_log_level_t level)
{
emit eventLog(node, level);
}
void FSHost::run(void) void FSHost::run(void)
{ {
switch_core_flag_t flags = SCF_USE_SQL | SCF_USE_AUTO_NAT; switch_core_flag_t flags = SCF_USE_SQL | SCF_USE_AUTO_NAT;
@ -150,6 +158,7 @@ void FSHost::run(void)
emit coreLoadingError(err); emit coreLoadingError(err);
} }
switch_log_bind_logger(loggerHandler, SWITCH_LOG_DEBUG, SWITCH_FALSE);
emit ready(); emit ready();
/* Go into the runtime loop. If the argument is true, this basically sets runtime.running = 1 and loops while that is set /* Go into the runtime loop. If the argument is true, this basically sets runtime.running = 1 and loops while that is set
@ -172,6 +181,8 @@ void FSHost::generalEventHandler(QSharedPointer<switch_event_t>event)
{ {
QString uuid = switch_event_get_header_nil(event.data(), "Unique-ID"); QString uuid = switch_event_get_header_nil(event.data(), "Unique-ID");
emit newEvent(event);
switch(event.data()->event_id) { switch(event.data()->event_id) {
case SWITCH_EVENT_CHANNEL_CREATE: /*1A - 17B*/ case SWITCH_EVENT_CHANNEL_CREATE: /*1A - 17B*/
{ {

View File

@ -45,6 +45,7 @@ public:
explicit FSHost(QObject *parent = 0); explicit FSHost(QObject *parent = 0);
switch_status_t sendCmd(const char *cmd, const char *args, QString *res); switch_status_t sendCmd(const char *cmd, const char *args, QString *res);
void generalEventHandler(QSharedPointer<switch_event_t>event); void generalEventHandler(QSharedPointer<switch_event_t>event);
void generalLoggerHandler(QSharedPointer<switch_log_node_t>node, switch_log_level_t level);
QSharedPointer<Call> getCallByUUID(QString uuid) { return _active_calls.value(uuid); } QSharedPointer<Call> getCallByUUID(QString uuid) { return _active_calls.value(uuid); }
QSharedPointer<Call> getCurrentActiveCall(); QSharedPointer<Call> getCurrentActiveCall();
QList<QSharedPointer<Account> > getAccounts() { return _accounts.values(); } QList<QSharedPointer<Account> > getAccounts() { return _accounts.values(); }
@ -64,6 +65,11 @@ signals:
void loadedModule(QString, QString); void loadedModule(QString, QString);
void ready(void); void ready(void);
/* Logging signals */
void eventLog(QSharedPointer<switch_log_node_t>, switch_log_level_t);
void newEvent(QSharedPointer<switch_event_t>);
/* Call signals */ /* Call signals */
void ringing(QSharedPointer<Call>); void ringing(QSharedPointer<Call>);
void answered(QSharedPointer<Call>); void answered(QSharedPointer<Call>);
@ -121,7 +127,7 @@ extern FSHost g_FSHost;
/* /*
Used to match callback from fs core. We dup the event and call the class Used to match callback from fs core. We dup the event and call the class
method callback to make use of the signal/slot infrastructure. method callback to make use of the signal/slot infrastructure.
*/ */
static void eventHandlerCallback(switch_event_t *event) static void eventHandlerCallback(switch_event_t *event)
{ {
switch_event_t *clone = NULL; switch_event_t *clone = NULL;
@ -131,4 +137,15 @@ static void eventHandlerCallback(switch_event_t *event)
} }
} }
/*
Used to propagate logs on the application
*/
static switch_status_t loggerHandler(const switch_log_node_t *node, switch_log_level_t level)
{
switch_log_node_t *clone = switch_log_node_dup(node);
QSharedPointer<switch_log_node_t> l(clone);
g_FSHost.generalLoggerHandler(l, level);
return SWITCH_STATUS_SUCCESS;
}
#endif // FSHOST_H #endif // FSHOST_H

View File

@ -36,7 +36,10 @@
MainWindow::MainWindow(QWidget *parent) : MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::MainWindow), ui(new Ui::MainWindow),
preferences(NULL) preferences(NULL),
_consoleWindow(NULL),
_stateDebugDialog(NULL)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -100,6 +103,8 @@ MainWindow::MainWindow(QWidget *parent) :
connect(ui->tableCalls, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(callTableDoubleClick(QTableWidgetItem*))); connect(ui->tableCalls, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(callTableDoubleClick(QTableWidgetItem*)));
connect(ui->action_Preferences, SIGNAL(triggered()), this, SLOT(prefTriggered())); connect(ui->action_Preferences, SIGNAL(triggered()), this, SLOT(prefTriggered()));
connect(ui->action_Exit, SIGNAL(triggered()), this, SLOT(close())); connect(ui->action_Exit, SIGNAL(triggered()), this, SLOT(close()));
connect(ui->actionConsole, SIGNAL(triggered()), this, SLOT(debugConsoleTriggered()));
connect(ui->actionEvents, SIGNAL(triggered()), this, SLOT(debugEventsTriggered()));
connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(showAbout())); connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(showAbout()));
connect(ui->actionSetDefaultAccount, SIGNAL(triggered(bool)), this, SLOT(setDefaultAccount())); connect(ui->actionSetDefaultAccount, SIGNAL(triggered(bool)), this, SLOT(setDefaultAccount()));
connect(sysTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(sysTrayActivated(QSystemTrayIcon::ActivationReason))); connect(sysTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(sysTrayActivated(QSystemTrayIcon::ActivationReason)));
@ -151,6 +156,28 @@ void MainWindow::setDefaultAccount()
settings.endGroup(); settings.endGroup();
} }
void MainWindow::debugEventsTriggered()
{
if (!_stateDebugDialog)
_stateDebugDialog = new StateDebugDialog();
_stateDebugDialog->raise();
_stateDebugDialog->show();
_stateDebugDialog->activateWindow();
}
void MainWindow::debugConsoleTriggered()
{
if (!_consoleWindow)
_consoleWindow = new ConsoleWindow();
_consoleWindow->raise();
_consoleWindow->show();
_consoleWindow->activateWindow();
}
void MainWindow::prefTriggered() void MainWindow::prefTriggered()
{ {
if (!preferences) if (!preferences)

View File

@ -40,6 +40,8 @@
#include <call.h> #include <call.h>
#include <account.h> #include <account.h>
#include "preferences/prefdialog.h" #include "preferences/prefdialog.h"
#include "debugtools/consolewindow.h"
#include "debugtools/statedebugdialog.h"
namespace Ui { namespace Ui {
class MainWindow; class MainWindow;
@ -80,11 +82,15 @@ private slots:
void accountStateChanged(QSharedPointer<Account>); void accountStateChanged(QSharedPointer<Account>);
void sysTrayActivated(QSystemTrayIcon::ActivationReason reason); void sysTrayActivated(QSystemTrayIcon::ActivationReason reason);
void updateCallTimers(); void updateCallTimers();
void debugConsoleTriggered();
void debugEventsTriggered();
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
QSignalMapper *dialpadMapper; QSignalMapper *dialpadMapper;
PrefDialog *preferences; PrefDialog *preferences;
ConsoleWindow *_consoleWindow;
StateDebugDialog * _stateDebugDialog;
QSystemTrayIcon *sysTray; QSystemTrayIcon *sysTray;
QTimer *callTimer; QTimer *callTimer;
}; };

View File

@ -375,7 +375,21 @@
</property> </property>
<addaction name="actionAbout"/> <addaction name="actionAbout"/>
</widget> </widget>
<widget class="QMenu" name="menuTools">
<property name="title">
<string>&amp;Tools</string>
</property>
<widget class="QMenu" name="menuDebug">
<property name="title">
<string>&amp;Debug</string>
</property>
<addaction name="actionConsole"/>
<addaction name="actionEvents"/>
</widget>
<addaction name="menuDebug"/>
</widget>
<addaction name="menu_File"/> <addaction name="menu_File"/>
<addaction name="menuTools"/>
<addaction name="menuHelp"/> <addaction name="menuHelp"/>
</widget> </widget>
<widget class="QToolBar" name="mainToolBar"> <widget class="QToolBar" name="mainToolBar">
@ -410,6 +424,16 @@
<string>Set the default account for dialing out.</string> <string>Set the default account for dialing out.</string>
</property> </property>
</action> </action>
<action name="actionConsole">
<property name="text">
<string>Console</string>
</property>
</action>
<action name="actionEvents">
<property name="text">
<string>Events</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources/> <resources/>