diff --git a/src/common/data.h b/src/common/data.h index 125ecca..6532c5e 100644 --- a/src/common/data.h +++ b/src/common/data.h @@ -1,405 +1,405 @@ /* This file is part of KNemo Copyright (C) 2004 Percy Leonhardt Copyright (C) 2009, 2010 John Stamp KNemo is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. KNemo 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DATA_H #define DATA_H #include #include #include #include #include #include #include class KCalendarSystem; /** * This file contains data structures used to store information about * an interface. It is shared between the daemon and the control center * module. * * @short Shared data structures * @author Percy Leonhardt */ namespace KNemoIface { enum Type { UnknownType, Ethernet, PPP }; enum IfaceState { UnknownState = 0, Unavailable = 1, Available = 2, Up = 4, Connected = 8, RxTraffic = 16, TxTraffic = 32, MaxState = 256 }; } namespace KNemoStats { enum TrafficUnits { UnitB = 0, UnitK, UnitM, UnitG }; enum TrafficDirection { TrafficIn = 0, TrafficOut, TrafficTotal }; enum WarnType { Peak = 0, Offpeak, PeakOffpeak }; enum PeriodUnits { Hour = 0, Day, Week, Month, BillPeriod, Year, HourArchive }; // Powers of 2 enum TrafficType { AllTraffic = 0, OffpeakTraffic = 1 }; -}; +} static const QLatin1String NETLOAD_THEME("netloadtheme"); static const QLatin1String TEXT_THEME("texttheme"); static const QLatin1String SYSTEM_THEME("systemtheme"); static const QLatin1String ICON_ERROR("error"); static const QLatin1String ICON_OFFLINE("offline"); static const QLatin1String ICON_IDLE("idle"); static const QLatin1String ICON_RX("receive"); static const QLatin1String ICON_TX("transmit"); static const QLatin1String ICON_RX_TX("transmit-receive"); // config groups static const QLatin1String confg_general("General"); static const QLatin1String confg_interface("Interface_"); static const QLatin1String confg_plotter("Plotter_"); static const QLatin1String confg_statsRule("StatsRule_"); static const QLatin1String confg_warnRule("WarnRule_"); static const QLatin1String conf_firstStart("FirstStart"); static const QLatin1String conf_autoStart("AutoStart"); static const QLatin1String conf_interfaces("Interfaces"); // interface icon static const QLatin1String conf_minVisibleState("MinVisibleState"); static const QLatin1String conf_trafficThreshold("TrafficThreshold"); static const QLatin1String conf_iconTheme("IconSet"); static const QLatin1String conf_barScale("BarScale"); static const QLatin1String conf_maxRate("MaxRate"); // interface statistics static const QLatin1String conf_activateStatistics("ActivateStatistics"); static const QLatin1String conf_calendarSystem("CalendarSystem"); static const QLatin1String conf_statsRules("StatsRules"); static const QLatin1String conf_warnRules("WarnRules"); // interface billing static const QLatin1String conf_statsStartDate("StartDate"); static const QLatin1String conf_statsPeriodUnits("PeriodUnits"); static const QLatin1String conf_statsPeriodCount("PeriodCount"); static const QLatin1String conf_logOffpeak("LogOffpeak"); static const QLatin1String conf_offpeakStartTime("OffpeakStartTime"); static const QLatin1String conf_offpeakEndTime("OffpeakEndTime"); static const QLatin1String conf_weekendIsOffpeak("WeekendIsOffpeak"); static const QLatin1String conf_weekendDayStart("WeekendDayStart"); static const QLatin1String conf_weekendDayEnd("WeekendDayEnd"); static const QLatin1String conf_weekendTimeStart("WeekendTimeStart"); static const QLatin1String conf_weekendTimeEnd("WeekendTimeEnd"); // warning static const QLatin1String conf_warnPeriodUnits("PeriodUnits"); static const QLatin1String conf_warnPeriodCount("PeriodCount"); static const QLatin1String conf_warnTrafficType("TrafficType"); static const QLatin1String conf_warnTrafficDirection("TrafficDirection"); static const QLatin1String conf_warnTrafficUnits("TrafficUnits"); static const QLatin1String conf_warnThreshold("Threshold"); static const QLatin1String conf_warnCustomText("CustomText"); // tooltip static const QLatin1String conf_toolTipContent("ToolTipContent"); // general static const QLatin1String conf_pollInterval("PollInterval"); static const QLatin1String conf_saveInterval("SaveInterval"); static const QLatin1String conf_useBitrate("UseBitrate"); static const QLatin1String conf_plotterPos("PlotterPos"); static const QLatin1String conf_plotterSize("PlotterSize"); static const QLatin1String conf_statisticsPos("StatisticsPos"); static const QLatin1String conf_statisticsSize("StatisticsSize"); static const QLatin1String conf_statusPos("StatusPos"); static const QLatin1String conf_statusSize("StatusSize"); static const QLatin1String conf_hourState("HourState"); static const QLatin1String conf_dayState("DayState"); static const QLatin1String conf_weekState("WeekState"); static const QLatin1String conf_monthState("MonthState"); static const QLatin1String conf_billingState("BillingState"); static const QLatin1String conf_yearState("YearState"); enum ToolTipEnums { INTERFACE = 0x00000001, //ALIAS = 0x00000002, STATUS = 0x00000004, UPTIME = 0x00000008, IP_ADDRESS = 0x00000010, SCOPE = 0x00000020, HW_ADDRESS = 0x00000040, PTP_ADDRESS = 0x00000080, RX_PACKETS = 0x00000100, TX_PACKETS = 0x00000200, RX_BYTES = 0x00000400, TX_BYTES = 0x00000800, ESSID = 0x00001000, MODE = 0x00002000, FREQUENCY = 0x00004000, BIT_RATE = 0x00008000, ACCESS_POINT = 0x00010000, LINK_QUALITY = 0x00020000, BCAST_ADDRESS = 0x00040000, GATEWAY = 0x00080000, DOWNLOAD_SPEED = 0x00100000, UPLOAD_SPEED = 0x00200000, NICK_NAME = 0x00400000, ENCRYPTION = 0x00800000 }; static const int defaultTip = STATUS | IP_ADDRESS | RX_BYTES | TX_BYTES | ESSID | LINK_QUALITY | DOWNLOAD_SPEED | UPLOAD_SPEED | ENCRYPTION; struct KNemoTheme { QString name; QString comment; QString internalName; }; struct AddrData { int afType; QString broadcastAddress; int scope; QString ipv6Flags; QString label; bool hasPeer; }; struct BackendData { BackendData() : status( KNemoIface::UnknownState ), index( -1 ), interfaceType( KNemoIface::UnknownType ), isWireless( false ), prevRxPackets( 0L ), prevTxPackets( 0L ), rxPackets( 0L ), txPackets( 0L ), prevRxBytes( 0L ), prevTxBytes( 0L ), incomingBytes( 0L ), outgoingBytes( 0L ), rxBytes( 0L ), txBytes( 0L ), isEncrypted( false ) {} int status; int prevStatus; int index; KNemoIface::Type interfaceType; bool isWireless; unsigned long prevRxPackets; unsigned long prevTxPackets; unsigned long rxPackets; unsigned long txPackets; unsigned long prevRxBytes; unsigned long prevTxBytes; unsigned long incomingBytes; unsigned long outgoingBytes; QMap addrData; QString hwAddress; QString ip4DefaultGateway; QString ip6DefaultGateway; QString rxString; QString txString; quint64 rxBytes; quint64 txBytes; QString essid; QString mode; QString frequency; QString channel; QString bitRate; QString linkQuality; QString accessPoint; QString prevAccessPoint; QString nickName; bool isEncrypted; }; struct GeneralSettings { GeneralSettings() : toolTipContent( defaultTip ), pollInterval( 1.0 ), saveInterval( 60 ), useBitrate( false ), statisticsDir( QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/knemo") ) {} int toolTipContent; double pollInterval; int saveInterval; bool useBitrate; QDir statisticsDir; }; class StatsRule { public: StatsRule() : periodCount( 1 ), periodUnits( KNemoStats::Month ), logOffpeak( false ), offpeakStartTime( QTime( 23, 0) ), offpeakEndTime( QTime( 7, 0) ), weekendIsOffpeak( false ), weekendDayStart( 5 ), weekendDayEnd( 1 ), weekendTimeStart( QTime( 23, 0) ), weekendTimeEnd( QTime( 7, 0) ) { } bool operator==( StatsRule &r ); bool isValid( KCalendarSystem *cal ); QDate startDate; int periodCount; int periodUnits; bool logOffpeak; QTime offpeakStartTime; QTime offpeakEndTime; bool weekendIsOffpeak; int weekendDayStart; int weekendDayEnd; QTime weekendTimeStart; QTime weekendTimeEnd; }; struct WarnRule { WarnRule() : periodUnits( KNemoStats::Month ), periodCount( 1 ), trafficType( KNemoStats::PeakOffpeak ), trafficDirection( KNemoStats::TrafficIn ), trafficUnits( KNemoStats::UnitG ), threshold( 5.0 ), warnDone( false ) { } bool operator==( WarnRule &r ) { if ( periodUnits == r.periodUnits && periodCount == r.periodCount && trafficType == r.trafficType && trafficDirection == r.trafficDirection && trafficUnits == r.trafficUnits && threshold == r.threshold ) return true; else return false; } int periodUnits; uint periodCount; int trafficType; int trafficDirection; int trafficUnits; double threshold; QString customText; bool warnDone; }; struct InterfaceSettings { InterfaceSettings() : barScale( false ), maxRate( 4 ), trafficThreshold( 0 ), minVisibleState( KNemoIface::UnknownState ), activateStatistics( false ), calendarSystem( KLocale::QDateCalendar ) {} QString iconTheme; bool barScale; unsigned int maxRate; unsigned int trafficThreshold; int minVisibleState; bool activateStatistics; QList statsRules; QList warnRules; KLocale::CalendarSystem calendarSystem; }; #ifndef __linux__ enum rt_scope_t { RT_SCOPE_UNIVERSE=0, RT_SCOPE_SITE=200, RT_SCOPE_LINK=253, RT_SCOPE_HOST=254, RT_SCOPE_NOWHERE=255 }; #endif static const double pollIntervals[] = { 0.1, 0.2, 0.25, 0.5, 1.0, 2.0 }; #endif // DATA_H diff --git a/src/knemod/interfacestatistics.cpp b/src/knemod/interfacestatistics.cpp index d9bb3a3..4530440 100644 --- a/src/knemod/interfacestatistics.cpp +++ b/src/knemod/interfacestatistics.cpp @@ -1,1118 +1,1118 @@ /* This file is part of KNemo Copyright (C) 2005, 2006 Percy Leonhardt Copyright (C) 2009, 2010 John Stamp KNemo is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. KNemo 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "global.h" #include "interface.h" #include "interfacestatistics.h" #include "statisticsmodel.h" #include "syncstats/statsfactory.h" #include "storage/sqlstorage.h" #include "storage/xmlstorage.h" static bool statsLessThan( const StatsRule& s1, const StatsRule& s2 ) { if ( s1.startDate < s2.startDate ) return true; else return false; } InterfaceStatistics::InterfaceStatistics( Interface* interface ) : QObject(), mInterface( interface ), mSaveTimer( new QTimer() ), mWarnTimer( new QTimer() ), mEntryTimer( new QTimer() ), mTrafficChanged( false ) { StatisticsModel * s = new StatisticsModel( KNemoStats::Hour, this ); mModels.insert( KNemoStats::Hour, s ); s = new StatisticsModel( KNemoStats::Day, this ); mModels.insert( KNemoStats::Day, s ); s = new StatisticsModel( KNemoStats::Week, this ); mModels.insert( KNemoStats::Week, s ); s = new StatisticsModel( KNemoStats::Month, this ); mModels.insert( KNemoStats::Month, s ); s = new StatisticsModel( KNemoStats::Year, this ); mModels.insert( KNemoStats::Year, s ); s = new StatisticsModel( KNemoStats::BillPeriod, this ); mModels.insert( KNemoStats::BillPeriod, s ); s = new StatisticsModel( KNemoStats::HourArchive, this ); mModels.insert( KNemoStats::HourArchive, s ); foreach ( StatisticsModel *s, mModels ) { mStorageData.saveFromId.insert( s->periodType(), 0 ); } connect( mSaveTimer, SIGNAL( timeout() ), this, SLOT( saveStatistics() ) ); connect( mWarnTimer, SIGNAL( timeout() ), this, SLOT( checkWarnings() ) ); connect( mEntryTimer, SIGNAL( timeout() ), this, SLOT( checkValidEntry() ) ); sql = new SqlStorage( mInterface->ifaceName() ); loadStats(); syncWithExternal( mStorageData.lastSaved ); configChanged(); } InterfaceStatistics::~InterfaceStatistics() { mSaveTimer->stop(); mWarnTimer->stop(); mEntryTimer->stop(); delete mSaveTimer; delete mWarnTimer; delete mEntryTimer; saveStatistics(); delete sql; QSqlDatabase::removeDatabase( mInterface->ifaceName() ); } void InterfaceStatistics::saveStatistics( bool fullSave ) { sql->saveStats( &mStorageData, &mModels, &mStatsRules, fullSave ); } bool InterfaceStatistics::loadStats() { bool loaded = false; if ( sql->dbExists() ) { loaded = sql->loadStats( &mStorageData, &mModels, &mStatsRules ); qSort( mStatsRules.begin(), mStatsRules.end(), statsLessThan ); } else { XmlStorage xml; loaded = xml.loadStats( mInterface->ifaceName(), &mStorageData, &mModels ); sql->createDb(); if ( loaded ) { hoursToArchive( QDateTime::currentDateTime() ); checkRebuild( mStorageData.calendar->calendarSystem(), true ); } } if ( !mStorageData.calendar ) { mStorageData.calendar = KCalendarSystem::create( mInterface->settings().calendarSystem ); foreach( StatisticsModel * s, mModels ) { s->setCalendar( mStorageData.calendar ); } } return loaded; } /********************************** * Stats Entry Generators * **********************************/ void InterfaceStatistics::resetWarnings( int modelType ) { for ( int i = 0; i < mInterface->settings().warnRules.count(); ++i ) { if ( modelType == mInterface->settings().warnRules[i].periodUnits ) mInterface->settings().warnRules[i].warnDone = false; } } void InterfaceStatistics::hoursToArchive( const QDateTime &dateTime ) { bool removedRow = false; StatisticsModel* hours = mModels.value( KNemoStats::Hour ); StatisticsModel* hourArchives = mModels.value( KNemoStats::HourArchive ); // Only 24 hours while ( hours->rowCount() ) { if ( hours->dateTime( 0 ) <= dateTime.addDays( -1 ) ) { QList row = hours->takeRow( 0 ); hourArchives->appendRow( row ); hourArchives->setId( mStorageData.nextHourId ); mStorageData.nextHourId++; removedRow = true; } else break; } if ( removedRow ) { for ( int i = 0; i < hours->rowCount(); ++i ) { hours->setId( i, i ); } mStorageData.saveFromId.insert( hours->periodType(), 0 ); mStorageData.saveFromId.insert( hourArchives->periodType(), hourArchives->id( 0 ) ); } } void InterfaceStatistics::genNewHour( const QDateTime &dateTime ) { hoursToArchive( dateTime ); StatisticsModel* hours = mModels.value( KNemoStats::Hour ); if ( hours->dateTime() == dateTime ) return; int ruleIndex = ruleForDate( dateTime.date() ); hours->createEntry( dateTime ); if ( ruleIndex >= 0 && isOffpeak( mStatsRules[ruleIndex], dateTime ) ) { hours->addTrafficType( KNemoStats::OffpeakTraffic ); } resetWarnings( hours->periodType() ); } bool InterfaceStatistics::genNewCalendarType( const QDate &date, const KNemoStats::PeriodUnits stype ) { if ( stype < KNemoStats::Day || stype > KNemoStats::Year ) return false; StatisticsModel * model = mModels.value( stype ); if ( model->rowCount() && model->date().addDays( model->days() ) > date ) return false; QDate newDate; int dayOf; int weekStartDay = mStorageData.calendar->weekStartDay(); switch ( stype ) { case KNemoStats::Day: newDate = date; break; case KNemoStats::Week: dayOf = mStorageData.calendar->dayOfWeek( date ); if ( dayOf >= weekStartDay ) newDate = date.addDays( weekStartDay - dayOf ); else newDate = date.addDays( weekStartDay - mStorageData.calendar->daysInWeek( date ) - dayOf ); break; case KNemoStats::Month: dayOf = mStorageData.calendar->day( date ); newDate = date.addDays( 1 - dayOf ); break; case KNemoStats::Year: dayOf = mStorageData.calendar->dayOfYear( date ); newDate = date.addDays( 1 - dayOf ); break; default: return false; } mModels.value( stype )->createEntry( QDateTime( newDate, QTime() ) ); resetWarnings( stype ); return true; } // Return true if at least one daily statistic entry is in a span of days bool InterfaceStatistics::daysInSpan( const QDate& date, int days ) { StatisticsModel *model = mModels.value( KNemoStats::Day ); if ( !model->rowCount() ) return false; QDate nextRuleStart = date.addDays( days ); for ( int i = model->rowCount() - 1; i >= 0; --i ) { // No others will be valid after this; stop early if ( model->date( i ) < date ) return false; if ( model->date( i ) < nextRuleStart && model->date( i ) >= date ) return true; } return false; } QDate InterfaceStatistics::nextBillPeriodStart( const StatsRule &rules, const QDate &date ) { QDate nextDate; QDate refDay; int modelType = rules.periodUnits; switch ( modelType ) { case KNemoStats::Day: nextDate = date.addDays( rules.periodCount ); break; case KNemoStats::Week: nextDate = date; for ( int i = 0; i < rules.periodCount; ++i ) nextDate = nextDate.addDays( mStorageData.calendar->daysInWeek( nextDate ) ); break; default:// KNemoStats::Month: nextDate = mStorageData.calendar->addMonths( date, rules.periodCount ); // Example: one month starting Jan 31 = Jan 31 -> Mar 1 // This seems the most common way to handle late start dates if ( mStorageData.calendar->day( nextDate ) < mStorageData.calendar->day( date ) ) nextDate = nextDate.addDays( 1 ); break; } return nextDate; } void InterfaceStatistics::genNewBillPeriod( const QDate &date ) { int ruleIndex = ruleForDate( date ); if ( ruleIndex < 0 ) return; StatisticsModel * billing = mModels.value( KNemoStats::BillPeriod ); QDate nextRuleStart; if ( mStatsRules.count() > ruleIndex+1 ) nextRuleStart = mStatsRules.at( ruleIndex+1 ).startDate; // Harder to find whether we should skip generating a new billing entry if ( nextRuleStart.isValid() && billing->rowCount() && billing->date().addDays( billing->days() ) >= nextRuleStart ) return; int days; // Given a calendar day and a billing period start date, find a // billing period that the day belongs in. QDate newDate; QDate nextStartDate; if ( !billing->rowCount() || billing->date() < mStatsRules.at( ruleIndex ).startDate ) { nextStartDate = mStatsRules.at( ruleIndex ).startDate; } else { nextStartDate = billing->date(); } do { newDate = nextStartDate; nextStartDate = nextBillPeriodStart( mStatsRules.at( ruleIndex ), newDate ); days = newDate.daysTo( nextStartDate ); } while ( nextStartDate <= date || !daysInSpan( newDate, days ) ); // Truncate a billing period if necessary if ( nextRuleStart.isValid() && nextRuleStart < nextStartDate ) days = newDate.daysTo( nextRuleStart ); if ( billing->rowCount() && newDate == billing->date() ) return; billing->createEntry( QDateTime( newDate, QTime() ), billing->rowCount(), days ); resetWarnings( KNemoStats::BillPeriod ); } // END STATS ENTRY GENERATORS void InterfaceStatistics::configChanged() { mSaveTimer->stop(); mWarnTimer->stop(); KLocale::CalendarSystem origCalendarSystem = KLocale::QDateCalendar; if ( mStorageData.calendar ) origCalendarSystem = mStorageData.calendar->calendarSystem(); if ( mInterface->settings().calendarSystem != origCalendarSystem ) { mStorageData.calendar = KCalendarSystem::create( mInterface->settings().calendarSystem ); foreach( StatisticsModel * s, mModels ) { s->setCalendar( mStorageData.calendar ); } if ( mStorageData.calendar ) { StatisticsModel *hours = mModels.value( KNemoStats::Hour ); StatisticsModel *days = mModels.value( KNemoStats::Day ); for ( int i = 0; i < days->rowCount(); ++i ) { days->updateDateText( i ); } for ( int i = 0; i < hours->rowCount(); ++i ) { hours->updateDateText( i ); } } } checkRebuild( origCalendarSystem ); if ( generalSettings->saveInterval > 0 ) { mSaveTimer->setInterval( generalSettings->saveInterval * 1000 ); mSaveTimer->start(); } foreach ( StatisticsModel * s, mModels ) { resetWarnings( s->periodType() ); } checkValidEntry(); mWarnTimer->setInterval( 2000 ); mWarnTimer->start(); } int InterfaceStatistics::ruleForDate( const QDate &date ) { for( int i = mStatsRules.count() - 1; i >= 0; --i ) { if ( date >= mStatsRules[i].startDate ) return i; } return -1; } void InterfaceStatistics::syncWithExternal( uint updated ) { ExternalStats *v = StatsFactory::stats( mInterface, mStorageData.calendar ); if ( !v ) return; v->importIfaceStats(); const StatisticsModel *syncDays = v->days(); const StatisticsModel *syncHours = v->hours(); StatisticsModel *days = mModels.value( KNemoStats::Day ); StatisticsModel *hours = mModels.value( KNemoStats::Hour ); QDateTime curDateTime = QDateTime( QDate::currentDate(), QTime( QDateTime::currentDateTime().time().hour(), 0 ) ); for ( int i = 0; i < syncHours->rowCount(); ++i ) { QDateTime syncDateTime = syncHours->dateTime( i ); if ( hours->rowCount() && hours->dateTime() > syncDateTime ) continue; if ( !hours->rowCount() || hours->dateTime() < syncDateTime ) genNewHour( syncDateTime ); foreach ( KNemoStats::TrafficType t, hours->trafficTypes() ) { hours->addRxBytes( syncHours->rxBytes( i ), t ); hours->addTxBytes( syncHours->txBytes( i ), t ); } } for ( int i = 0; i < syncDays->rowCount(); ++i ) { QDate syncDate = syncDays->date( i ); if ( days->rowCount() && days->date() > syncDate ) continue; if ( !days->rowCount() || days->date() < syncDate ) { genNewCalendarType( syncDate, KNemoStats::Day ); genNewCalendarType( syncDate, KNemoStats::Week ); genNewCalendarType( syncDate, KNemoStats::Month ); genNewCalendarType( syncDate, KNemoStats::Year ); genNewBillPeriod( syncDate ); } foreach( StatisticsModel * s, mModels ) { if ( s->periodType() == KNemoStats::Hour || s->periodType() == KNemoStats::HourArchive ) continue; s->addRxBytes( v->addBytes( s->rxBytes(), syncDays->rxBytes( i ) ) ); s->addTxBytes( v->addBytes( s->txBytes(), syncDays->txBytes( i ) ) ); for ( int j = hours->rowCount() - 1; j >= 0; --j ) { if ( hours->date( j ) == syncDate ) { foreach( KNemoStats::TrafficType t, hours->trafficTypes( j ) ) { if ( t == KNemoStats::AllTraffic ) continue; s->addRxBytes( hours->rxBytes( j, t ), t ); s->addTxBytes( hours->txBytes( j, t ), t ); } } else if ( hours->date( j ) < syncDate ) break; } } } StatsPair lag = v->addLagged( updated, days ); if ( lag.rxBytes > 0 || lag.txBytes > 0 ) { if ( lag.rxBytes || lag.txBytes ) { genNewHour( curDateTime ); genNewCalendarType( curDateTime.date(), KNemoStats::Day ); genNewCalendarType( curDateTime.date(), KNemoStats::Week ); genNewCalendarType( curDateTime.date(), KNemoStats::Month ); genNewCalendarType( curDateTime.date(), KNemoStats::Year ); genNewBillPeriod( curDateTime.date() ); foreach ( StatisticsModel * s, mModels ) { if ( s->periodType() == KNemoStats::HourArchive ) continue; foreach ( KNemoStats::TrafficType t, hours->trafficTypes() ) { s->addRxBytes( lag.rxBytes, t ); s->addTxBytes( lag.txBytes, t ); } } } } delete v; } bool InterfaceStatistics::isOffpeak( const StatsRule &rules, const QDateTime &curDT ) { if ( !rules.logOffpeak ) return false; bool isOffpeak = false; QTime curHour = QTime( curDT.time().hour(), 0 ); // This block just tests weekly hours if ( rules.offpeakStartTime < rules.offpeakEndTime && curHour >= rules.offpeakStartTime && curHour < rules.offpeakEndTime ) { isOffpeak = true; } else if ( rules.offpeakStartTime > rules.offpeakEndTime && ( curHour >= rules.offpeakStartTime || curHour < rules.offpeakEndTime ) ) { isOffpeak = true; } if ( rules.weekendIsOffpeak ) { int dow = mStorageData.calendar->dayOfWeek( curDT.date() ); if ( rules.weekendDayStart <= rules.weekendDayEnd && rules.weekendDayStart <= dow ) { QDateTime dayBegin = curDT.addDays( dow - rules.weekendDayStart ); dayBegin = QDateTime( dayBegin.date(), rules.weekendTimeStart ); QDateTime end = curDT.addDays( rules.weekendDayEnd - dow ); end = QDateTime( end.date(), rules.weekendTimeEnd ); if ( dayBegin <= curDT && curDT < end ) isOffpeak = true; } else if ( rules.weekendDayStart > rules.weekendDayEnd && ( dow >= rules.weekendDayStart || dow <= rules.weekendDayEnd ) ) { QDateTime dayBegin = curDT.addDays( rules.weekendDayStart - dow ); dayBegin = QDateTime( dayBegin.date(), rules.weekendTimeStart ); QDateTime end = curDT.addDays( mStorageData.calendar->daysInWeek( curDT.date() ) - dow + rules.weekendDayEnd ); end = QDateTime( end.date(), rules.weekendTimeEnd ); if ( dayBegin <= curDT && curDT < end ) isOffpeak = true; } } return isOffpeak; } /************************************** * Rebuilding Statistics * **************************************/ int InterfaceStatistics::rebuildHours( StatisticsModel *s, const StatsRule &rules, const QDate &start, const QDate &nextRuleStart ) { if ( !s->rowCount() ) return 0; int i = s->rowCount(); while ( i > 0 && s->date( i - 1 ) >= start ) { i--; if ( nextRuleStart.isValid() && s->date( i ) >= nextRuleStart ) continue; s->resetTrafficTypes( i ); if ( isOffpeak( rules, s->dateTime( i ) ) ) { s->setTraffic( i, s->rxBytes( i ), s->txBytes( i ), KNemoStats::OffpeakTraffic ); s->addTrafficType( KNemoStats::OffpeakTraffic, i ); } } if ( mStorageData.saveFromId.value( s->periodType() ) > s->id( i ) ) { mStorageData.saveFromId.insert( s->periodType(), s->id( i ) ); } return i; } int InterfaceStatistics::rebuildDay( int dayIndex, int hourIndex, StatisticsModel *hours ) { QMap > dayTraffic; StatisticsModel *days = mModels.value( KNemoStats::Day ); while ( hourIndex >= 0 && hours->date( hourIndex ) > days->date( dayIndex ).addDays( 1 ) ) { --hourIndex; } while ( hourIndex >= 0 && hours->date( hourIndex ) == days->date( dayIndex ) ) { foreach ( KNemoStats::TrafficType t, hours->trafficTypes( hourIndex ) ) { if ( t == KNemoStats::AllTraffic ) continue; quint64 rx = hours->rxBytes( hourIndex, t ) + dayTraffic.value( t ).first; quint64 tx = hours->txBytes( hourIndex, t ) + dayTraffic.value( t ).second; dayTraffic.insert( t, QPair( rx, tx ) ); } --hourIndex; } foreach (KNemoStats::TrafficType t, dayTraffic.keys() ) { days->setTraffic( dayIndex, dayTraffic.value( t ).first, dayTraffic.value( t ).second, t ); days->addTrafficType( t, dayIndex ); } return hourIndex; } // A rebuild of hours and days never changes the number of entries // We just change what bytes count as off-peak void InterfaceStatistics::rebuildBaseUnits( const StatsRule &rules, const QDate &start, const QDate &nextRuleStart ) { int hIndex = 0; int haIndex = 0; StatisticsModel *hours = mModels.value( KNemoStats::Hour ); StatisticsModel *hourArchives = mModels.value( KNemoStats::HourArchive ); StatisticsModel *days = mModels.value( KNemoStats::Day ); sql->loadHourArchives( hourArchives, start, nextRuleStart ); if ( hourArchives->rowCount() ) mStorageData.saveFromId.insert( hourArchives->periodType(), hourArchives->id( 0 ) ); rebuildHours( hourArchives, rules, start, nextRuleStart ); rebuildHours( hours, rules, start, nextRuleStart ); if ( hours->rowCount() ) hIndex = hours->rowCount() - 1; if ( hourArchives->rowCount() ) haIndex = hourArchives->rowCount() - 1; if ( !days->rowCount() ) return; int dayIndex = days->rowCount(); while ( dayIndex > 0 && days->date( dayIndex - 1 ) >= start ) { dayIndex--; if ( nextRuleStart.isValid() && days->date( dayIndex ) >= nextRuleStart ) continue; days->resetTrafficTypes( dayIndex ); if ( rules.logOffpeak ) { haIndex = rebuildDay( dayIndex, haIndex, hourArchives ); hIndex = rebuildDay( dayIndex, hIndex, hours ); } } if ( mStorageData.saveFromId.value( days->periodType() ) > days->id( dayIndex ) ) { mStorageData.saveFromId.insert( days->periodType(), days->id( dayIndex ) ); } } /** * Given a model with statistics of a certain unit (year, month, week, etc.) * and a requested rebuild date, how far back to we actually need to go * to accuratly rebuild statistics from the daily stats. */ QDate InterfaceStatistics::prepareRebuild( StatisticsModel* statistics, const QDate &startDate ) { QDate newStartDate = startDate; if ( statistics->periodType() <= KNemoStats::Day || statistics->periodType() > KNemoStats::Year ) return newStartDate; for ( int i = 0; i < statistics->rowCount(); ++i ) { int days = statistics->days( i ); QDate nextPeriodStart = statistics->date( i ).addDays( days ); if ( statistics->periodType() == KNemoStats::BillPeriod ) { // Have to check if a billing period's truncation changed int ruleIndex = ruleForDate( statistics->date( i ) ); if ( ruleIndex < 0 ) break; QDate nextFullPeriodStart = nextBillPeriodStart( mStatsRules.at( ruleIndex ), statistics->date( i ) ); if ( nextFullPeriodStart > nextPeriodStart ) { if ( mStatsRules.count() == ruleIndex+1 || mStatsRules.at( ruleIndex + 1 ).startDate != nextPeriodStart ) { // Truncation changed // This will make sure the entry gets rebuilt nextPeriodStart = nextFullPeriodStart; } } } if ( nextPeriodStart > startDate ) { if ( statistics->date( i ) < startDate ) { newStartDate = statistics->date( i ); } if ( statistics->rowCount() && mStorageData.saveFromId.value( statistics->periodType() ) > statistics->id( i ) ) { mStorageData.saveFromId.insert( statistics->periodType(), statistics->id( i ) ); } statistics->removeRows( i, statistics->rowCount() - i ); break; } } return newStartDate; } void InterfaceStatistics::amendStats( int i, const StatisticsModel *source, StatisticsModel* dest ) { foreach ( KNemoStats::TrafficType t, source->trafficTypes( i ) ) { dest->addRxBytes( source->rxBytes( i, t ), t ); dest->addTxBytes( source->txBytes( i, t ), t ); dest->addTrafficType( t ); } } void InterfaceStatistics::rebuildCalendarPeriods( const QDate &requestedStart, bool weekOnly ) { QDate weekStart; QDate monthStart; QDate walkbackDate; QList s; weekStart = prepareRebuild( mModels.value( KNemoStats::Week), requestedStart ); s.append( weekStart ); if ( !weekOnly ) { monthStart = prepareRebuild( mModels.value( KNemoStats::Month), requestedStart ); s.append( monthStart ); } // Now find how far back we'll need to go qSort( s ); walkbackDate = s.first(); StatisticsModel *days = mModels.value( KNemoStats::Day ); for ( int i = 0; i < days->rowCount(); ++i ) { QDate day = days->date( i ); if ( day < walkbackDate ) continue; if ( day >= weekStart ) { genNewCalendarType( day, KNemoStats::Week ); amendStats( i, mModels.value( KNemoStats::Day ), mModels.value( KNemoStats::Week ) ); } if ( !weekOnly && day >= monthStart ) { genNewCalendarType( day, KNemoStats::Month ); amendStats( i, mModels.value( KNemoStats::Day ), mModels.value( KNemoStats::Month ) ); } } if ( weekOnly ) return; // Build years from months...save time QDate yearStart = prepareRebuild( mModels.value( KNemoStats::Year ), requestedStart ); StatisticsModel *months = mModels.value( KNemoStats::Month ); for ( int i = 0; i < months->rowCount(); ++i ) { QDate day = months->date( i ); if ( day < yearStart ) continue; genNewCalendarType( day, KNemoStats::Year ); amendStats( i, mModels.value( KNemoStats::Month ), mModels.value( KNemoStats::Year ) ); } } void InterfaceStatistics::rebuildBillPeriods( const QDate &requestedStart ) { QDate walkbackDate; StatisticsModel *days = mModels.value( KNemoStats::Day ); StatisticsModel *billPeriods = mModels.value( KNemoStats::BillPeriod ); if ( billPeriods->rowCount() ) walkbackDate = prepareRebuild( mModels.value( KNemoStats::BillPeriod), requestedStart ); else walkbackDate = days->date( 0 ); for ( int i = 0; i < days->rowCount(); ++i ) { QDate day = days->date( i ); if ( day >= walkbackDate ) { genNewBillPeriod( day ); amendStats( i, mModels.value( KNemoStats::Day ), mModels.value( KNemoStats::BillPeriod ) ); } } } void InterfaceStatistics::prependStatsRule( QList &rules ) { qSort( rules.begin(), rules.end(), statsLessThan ); StatisticsModel * days = mModels.value( KNemoStats::Day ); if ( rules.count() == 0 || ( days->rowCount() > 0 && rules[0].startDate > days->date( 0 ) ) ) { QDate date; if ( days->rowCount() ) date = days->date( 0 ); else date = QDate::currentDate(); StatsRule s; s.startDate = date.addDays( 1 - mStorageData.calendar->day( date ) ); rules.prepend( s ); } } void InterfaceStatistics::checkRebuild( const KLocale::CalendarSystem oldCalendar, bool force ) { QList newRules = mInterface->settings().statsRules; bool forceWeek = false; if ( oldCalendar != mInterface->settings().calendarSystem ) { StatisticsModel *hours = mModels.value( KNemoStats::Hour ); StatisticsModel *days = mModels.value( KNemoStats::Day ); for ( int i = 0; i < days->rowCount(); ++i ) { days->updateDateText( i ); } for ( int i = 0; i < hours->rowCount(); ++i ) { hours->updateDateText( i ); } force = true; } if ( mModels.value( KNemoStats::Week )->rowCount() ) { QDate testDate = mModels.value( KNemoStats::Week )->date( 0 ); if ( mStorageData.calendar->dayOfWeek( testDate ) != mStorageData.calendar->weekStartDay() ) forceWeek = true; } bool doBilling = newRules.count(); int oldRuleCount = mStatsRules.count(); QDate bpBeginDate; if ( !doBilling ) { mModels.value( KNemoStats::BillPeriod )->clearRows(); mStorageData.saveFromId.insert( KNemoStats::BillPeriod, 0 ); } // This is just a dummy for calculation prependStatsRule( newRules ); prependStatsRule( mStatsRules ); if ( !oldRuleCount && mStatsRules[0] == newRules[0] && newRules.count() > mStatsRules.count() ) bpBeginDate = mModels.value( KNemoStats::Day )->date( 0 ); int j = 0; QDate recalcPos; for ( int i = 0; i < newRules.count(); ++i ) { bool rulesMatch = ( newRules[i] == mStatsRules[j] ); QDate nextRuleStart; if ( !rulesMatch ) { if ( newRules.count() > i + 1 ) nextRuleStart = newRules[i+1].startDate; if ( !recalcPos.isValid() ) recalcPos = newRules[i].startDate; rebuildBaseUnits( newRules[i], newRules[i].startDate, nextRuleStart ); } else { // rules match, now scan forward to see if we need to extend new rule if ( newRules.count() > i + 1 ) { int first = -1; int k; // Here we want to skip over any intermediary old rules that will // get taken care of when we recalculate this section. for ( k = 0; k < mStatsRules.count(); ++k ) { if ( mStatsRules[k].startDate > newRules[i].startDate && mStatsRules[k].startDate < newRules[i+1].startDate ) { if ( first < 0 ) first = k; j = k; if ( !recalcPos.isValid() ) recalcPos = mStatsRules[j].startDate; } } if ( first >= 0 ) { rebuildBaseUnits( newRules[i], mStatsRules[first].startDate, newRules[i+1].startDate ); } } // We're out of new rules but there's more old ones // so rebuild from next old rule's date using final new rule. else if ( mStatsRules.count() > j + 1 ) { if ( !recalcPos.isValid() ) recalcPos = mStatsRules[j+1].startDate; rebuildBaseUnits( newRules[i], recalcPos, nextRuleStart ); } if ( mStatsRules.count() > j + 1 ) ++j; } } /* now do the rest */ mStatsRules = newRules; if ( force ) recalcPos = mModels.value( KNemoStats::Day )->date( 0 ); if ( recalcPos.isValid() ) { rebuildCalendarPeriods( recalcPos ); if ( doBilling ) { rebuildBillPeriods( recalcPos ); } } else if ( forceWeek ) { rebuildCalendarPeriods( mModels.value( KNemoStats::Day )->date( 0 ), true ); } mStatsRules = mInterface->settings().statsRules; if ( mStatsRules.count() ) prependStatsRule( mStatsRules ); if ( recalcPos.isValid() ) { saveStatistics( true ); } mTrafficChanged = true; emit currentEntryChanged(); } // END REBUILDING STATISTICS void InterfaceStatistics::checkValidEntry() { mEntryTimer->stop(); QDateTime curDateTime = QDateTime::currentDateTime(); QDate curDate = curDateTime.date(); StatisticsModel *days = mModels.value( KNemoStats::Day ); StatisticsModel *hours = mModels.value( KNemoStats::Hour ); if ( !hours->rowCount() || hours->dateTime().addSecs( 3600 ) <= curDateTime ) { genNewHour( QDateTime( curDate, QTime( curDateTime.time().hour(), 0 ) ) ); } if ( !days->rowCount() || days->date() < curDate ) { genNewCalendarType( curDate, KNemoStats::Day ); genNewCalendarType( curDate, KNemoStats::Week ); genNewCalendarType( curDate, KNemoStats::Month ); genNewCalendarType( curDate, KNemoStats::Year ); genNewBillPeriod( curDate ); // The fancy short date may need updating for ( int i = 0; i < hours->rowCount(); ++i ) hours->updateDateText( i ); } foreach ( StatisticsModel * s, mModels ) { if ( s->periodType() == KNemoStats::HourArchive || s->periodType() == KNemoStats::Hour ) continue; foreach ( KNemoStats::TrafficType t, hours->trafficTypes() ) { s->addTrafficType( t ); } } QDateTime ndt = curDateTime.addSecs( 3600 - curDateTime.time().minute()*60 - curDateTime.time().second() ); int secs = curDateTime.secsTo( ndt ); mEntryTimer->setInterval( secs * 1000 ); mEntryTimer->start(); } void InterfaceStatistics::checkWarnings() { if ( !mTrafficChanged ) return; mTrafficChanged = false; QList warn = mInterface->settings().warnRules; for ( int wi=0; wi < warn.count(); ++wi ) { - if ( warn[wi].warnDone || !warn[wi].threshold > 0.0 ) + if ( warn[wi].warnDone || warn[wi].threshold == 0 ) continue; quint64 total = 0; StatisticsModel *model = 0; model = mModels.value( warn[wi].periodUnits ); if ( !model ) return; int lowerIndex = model->rowCount() - warn[wi].periodCount; for ( int i = model->rowCount() - 1; i >= 0; --i ) { if ( i >= lowerIndex ) { switch ( warn[wi].trafficDirection ) { case KNemoStats::TrafficIn: if ( warn[wi].trafficType == KNemoStats::PeakOffpeak ) total += model->rxBytes( i ); else if ( warn[wi].trafficType == KNemoStats::Offpeak ) total += model->rxBytes( i, KNemoStats::OffpeakTraffic ); else total += model->rxBytes( i ) - model->rxBytes( i, KNemoStats::OffpeakTraffic ); break; case KNemoStats::TrafficOut: if ( warn[wi].trafficType == KNemoStats::PeakOffpeak ) total += model->txBytes( i ); else if ( warn[wi].trafficType == KNemoStats::Offpeak ) total += model->txBytes( i, KNemoStats::OffpeakTraffic ); else total += model->txBytes( i ) - model->txBytes( i, KNemoStats::OffpeakTraffic ); break; default: if ( warn[wi].trafficType == KNemoStats::PeakOffpeak ) total += model->totalBytes( i ); else if ( warn[wi].trafficType == KNemoStats::Offpeak ) total += model->totalBytes( i, KNemoStats::OffpeakTraffic ); else total += model->totalBytes( i ) - model->totalBytes( i, KNemoStats::OffpeakTraffic ); } } else break; } int warnMult = pow( 1024, warn[wi].trafficUnits ); quint64 thresholdBytes = warn[wi].threshold * warnMult; if ( total > thresholdBytes ) { emit warnTraffic( warn[wi].customText, thresholdBytes, total ); mInterface->settings().warnRules[wi].warnDone = true; } } } /****************************** * Public Interface * ******************************/ void InterfaceStatistics::clearStatistics() { foreach( StatisticsModel * s, mModels ) s->clearRows(); mStorageData.nextHourId = 0; foreach ( StatisticsModel *s, mModels ) { mStorageData.saveFromId.insert( s->periodType(), 0 ); } sql->clearStats( &mStorageData ); checkValidEntry(); mTrafficChanged = true; emit currentEntryChanged(); } void InterfaceStatistics::addRxBytes( unsigned long bytes ) { if ( bytes == 0 ) return; foreach( StatisticsModel * s, mModels ) { if ( s->periodType() == KNemoStats::HourArchive ) continue; foreach ( KNemoStats::TrafficType t, mModels.value( KNemoStats::Hour )->trafficTypes() ) { s->addRxBytes( bytes, t ); } } mTrafficChanged = true; emit currentEntryChanged(); } void InterfaceStatistics::addTxBytes( unsigned long bytes ) { if ( bytes == 0 ) return; foreach( StatisticsModel * s, mModels ) { if ( s->periodType() == KNemoStats::HourArchive ) continue; foreach ( KNemoStats::TrafficType t, mModels.value( KNemoStats::Hour )->trafficTypes() ) { s->addTxBytes( bytes, t ); } } mTrafficChanged = true; emit currentEntryChanged(); } #include "moc_interfacestatistics.cpp"