| File: | ui/qt/lte_rlc_statistics_dialog.cpp |
| Warning: | line 892, column 5 Potential leak of memory pointed to by 'ue_ti' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* lte_rlc_statistics_dialog.cpp | |||
| 2 | * | |||
| 3 | * Wireshark - Network traffic analyzer | |||
| 4 | * By Gerald Combs <[email protected]> | |||
| 5 | * Copyright 1998 Gerald Combs | |||
| 6 | * | |||
| 7 | * SPDX-License-Identifier: GPL-2.0-or-later | |||
| 8 | */ | |||
| 9 | ||||
| 10 | #include "lte_rlc_statistics_dialog.h" | |||
| 11 | ||||
| 12 | #include <epan/packet.h> | |||
| 13 | #include <epan/strutil.h> | |||
| 14 | #include <epan/tap.h> | |||
| 15 | ||||
| 16 | #include <QFormLayout> | |||
| 17 | #include <QTreeWidgetItem> | |||
| 18 | #include <QPushButton> | |||
| 19 | ||||
| 20 | #include "main_application.h" | |||
| 21 | ||||
| 22 | #include "ui/recent.h" | |||
| 23 | ||||
| 24 | // TODO: have never tested in a live capture. | |||
| 25 | ||||
| 26 | enum { | |||
| 27 | col_rat_, | |||
| 28 | col_ueid_, | |||
| 29 | col_mode_, // channel only | |||
| 30 | col_priority_, // channel only | |||
| 31 | col_ul_frames_, | |||
| 32 | col_ul_bytes_, | |||
| 33 | col_ul_mb_s_, | |||
| 34 | col_ul_acks_, | |||
| 35 | col_ul_nacks_, | |||
| 36 | col_ul_missing_, | |||
| 37 | col_dl_frames_, | |||
| 38 | col_dl_bytes_, | |||
| 39 | col_dl_mb_s_, | |||
| 40 | col_dl_acks_, | |||
| 41 | col_dl_nacks_, | |||
| 42 | col_dl_missing_ | |||
| 43 | }; | |||
| 44 | ||||
| 45 | enum { | |||
| 46 | rlc_ue_row_type_ = 1000, | |||
| 47 | rlc_channel_row_type_ | |||
| 48 | }; | |||
| 49 | ||||
| 50 | /* Calculate and return a bandwidth figure, in Mbs */ | |||
| 51 | static double calculate_bw(const nstime_t *start_time, const nstime_t *stop_time, uint32_t bytes) | |||
| 52 | { | |||
| 53 | /* Can only calculate bandwidth if have time delta */ | |||
| 54 | if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) { | |||
| 55 | double elapsed_ms = (((double)stop_time->secs - start_time->secs) * 1000) + | |||
| 56 | (((double)stop_time->nsecs - start_time->nsecs) / 1000000); | |||
| 57 | ||||
| 58 | /* Only really meaningful if have a few frames spread over time... | |||
| 59 | For now at least avoid dividing by something very close to 0.0 */ | |||
| 60 | if (elapsed_ms < 2.0) { | |||
| 61 | return 0.0f; | |||
| 62 | } | |||
| 63 | ||||
| 64 | // N.B. very small values will display as scientific notation, but rather that than show 0 | |||
| 65 | // when there is some traffic.. | |||
| 66 | return ((bytes * 8) / elapsed_ms) / 1000; | |||
| 67 | } | |||
| 68 | else { | |||
| 69 | return 0.0f; | |||
| 70 | } | |||
| 71 | } | |||
| 72 | ||||
| 73 | ||||
| 74 | // Stats kept for one channel. | |||
| 75 | typedef struct rlc_channel_stats { | |||
| 76 | uint8_t rlcMode; | |||
| 77 | uint8_t priority; | |||
| 78 | uint16_t channelType; | |||
| 79 | uint16_t channelId; | |||
| 80 | ||||
| 81 | uint32_t UL_frames; | |||
| 82 | uint32_t UL_bytes; | |||
| 83 | nstime_t UL_time_start; | |||
| 84 | nstime_t UL_time_stop; | |||
| 85 | bool UL_has_data; // i.e. not just ACKs for DL. | |||
| 86 | ||||
| 87 | uint32_t DL_frames; | |||
| 88 | uint32_t DL_bytes; | |||
| 89 | nstime_t DL_time_start; | |||
| 90 | nstime_t DL_time_stop; | |||
| 91 | bool DL_has_data; // i.e. not just ACKs for UL. | |||
| 92 | ||||
| 93 | uint32_t UL_acks; | |||
| 94 | uint32_t UL_nacks; | |||
| 95 | ||||
| 96 | uint32_t DL_acks; | |||
| 97 | uint32_t DL_nacks; | |||
| 98 | ||||
| 99 | uint32_t UL_missing; | |||
| 100 | uint32_t DL_missing; | |||
| 101 | } rlc_channel_stats; | |||
| 102 | ||||
| 103 | //------------------------------------------------------------------- | |||
| 104 | // Channel item. | |||
| 105 | //------------------------------------------------------------------- | |||
| 106 | class RlcChannelTreeWidgetItem : public QTreeWidgetItem | |||
| 107 | { | |||
| 108 | public: | |||
| 109 | RlcChannelTreeWidgetItem(QTreeWidgetItem *parent, | |||
| 110 | uint8_t rat, | |||
| 111 | unsigned ueid, | |||
| 112 | unsigned mode, | |||
| 113 | unsigned channelType, unsigned channelId) : | |||
| 114 | QTreeWidgetItem(parent, rlc_channel_row_type_), | |||
| 115 | rat_(rat), | |||
| 116 | ueid_(ueid), | |||
| 117 | channelType_(channelType), | |||
| 118 | channelId_(channelId), | |||
| 119 | mode_(mode), | |||
| 120 | priority_(0) | |||
| 121 | { | |||
| 122 | QString mode_str; | |||
| 123 | switch (mode_) { | |||
| 124 | case RLC_TM_MODE1: | |||
| 125 | mode_str = QObject::tr("TM"); | |||
| 126 | break; | |||
| 127 | case RLC_UM_MODE2: | |||
| 128 | mode_str = QObject::tr("UM"); | |||
| 129 | break; | |||
| 130 | case RLC_AM_MODE4: | |||
| 131 | mode_str = QObject::tr("AM"); | |||
| 132 | break; | |||
| 133 | case RLC_PREDEF8: | |||
| 134 | mode_str = QObject::tr("Predef"); | |||
| 135 | break; | |||
| 136 | ||||
| 137 | default: | |||
| 138 | mode_str = QObject::tr("Unknown (%1)").arg(mode_); | |||
| 139 | break; | |||
| 140 | } | |||
| 141 | ||||
| 142 | // Set name of channel. | |||
| 143 | switch (channelType) { | |||
| 144 | case CHANNEL_TYPE_CCCH1: | |||
| 145 | setText(col_ueid_, QObject::tr("CCCH")); | |||
| 146 | break; | |||
| 147 | case CHANNEL_TYPE_SRB4: | |||
| 148 | setText(col_ueid_, QObject::tr("SRB-%1").arg(channelId)); | |||
| 149 | break; | |||
| 150 | case CHANNEL_TYPE_DRB5: | |||
| 151 | setText(col_ueid_, QObject::tr("DRB-%1").arg(channelId)); | |||
| 152 | break; | |||
| 153 | ||||
| 154 | default: | |||
| 155 | setText(col_ueid_, QObject::tr("Unknown")); | |||
| 156 | break; | |||
| 157 | } | |||
| 158 | ||||
| 159 | // Zero out stats. | |||
| 160 | memset(&stats_, 0, sizeof(stats_)); | |||
| 161 | ||||
| 162 | // TODO: could change, but should only reset string if changes. | |||
| 163 | setText(col_mode_, mode_str); | |||
| 164 | } | |||
| 165 | ||||
| 166 | // Update UE/channels from tap info. | |||
| 167 | void update(const rlc_3gpp_tap_info *tap_info) { | |||
| 168 | ||||
| 169 | // Copy these fields into UE stats. | |||
| 170 | if (tap_info->rlcMode != stats_.rlcMode) { | |||
| 171 | stats_.rlcMode = tap_info->rlcMode; | |||
| 172 | // TODO: update the column string! | |||
| 173 | } | |||
| 174 | ||||
| 175 | // TODO: these 2 really shouldn't change!! | |||
| 176 | stats_.channelType = tap_info->channelType; | |||
| 177 | stats_.channelId = tap_info->channelId; | |||
| 178 | ||||
| 179 | if (tap_info->priority != 0) { | |||
| 180 | priority_ = tap_info->priority; | |||
| 181 | // TODO: update the column string! | |||
| 182 | } | |||
| 183 | ||||
| 184 | if (tap_info->direction == DIRECTION_UPLINK0) { | |||
| 185 | // Update time range. | |||
| 186 | if (stats_.UL_frames == 0) { | |||
| 187 | stats_.UL_time_start = tap_info->rlc_time; | |||
| 188 | } | |||
| 189 | stats_.UL_time_stop = tap_info->rlc_time; | |||
| 190 | ||||
| 191 | stats_.UL_frames++; | |||
| 192 | stats_.UL_bytes += tap_info->pduLength; | |||
| 193 | stats_.UL_nacks += tap_info->noOfNACKs; | |||
| 194 | stats_.UL_missing += tap_info->missingSNs; | |||
| 195 | if (tap_info->isControlPDU) { | |||
| 196 | stats_.UL_acks++; | |||
| 197 | } | |||
| 198 | else { | |||
| 199 | stats_.UL_has_data = true; | |||
| 200 | } | |||
| 201 | } | |||
| 202 | else { | |||
| 203 | // Update time range. | |||
| 204 | if (stats_.DL_frames == 0) { | |||
| 205 | stats_.DL_time_start = tap_info->rlc_time; | |||
| 206 | } | |||
| 207 | stats_.DL_time_stop = tap_info->rlc_time; | |||
| 208 | ||||
| 209 | stats_.DL_frames++; | |||
| 210 | stats_.DL_bytes += tap_info->pduLength; | |||
| 211 | stats_.DL_nacks += tap_info->noOfNACKs; | |||
| 212 | stats_.DL_missing += tap_info->missingSNs; | |||
| 213 | if (tap_info->isControlPDU) { | |||
| 214 | stats_.DL_acks++; | |||
| 215 | } | |||
| 216 | else { | |||
| 217 | stats_.DL_has_data = true; | |||
| 218 | } | |||
| 219 | } | |||
| 220 | } | |||
| 221 | ||||
| 222 | // Draw channel entry. | |||
| 223 | void draw() { | |||
| 224 | // Calculate bandwidths. | |||
| 225 | double UL_bw = calculate_bw(&stats_.UL_time_start, | |||
| 226 | &stats_.UL_time_stop, | |||
| 227 | stats_.UL_bytes); | |||
| 228 | double DL_bw = calculate_bw(&stats_.DL_time_start, | |||
| 229 | &stats_.DL_time_stop, | |||
| 230 | stats_.DL_bytes); | |||
| 231 | ||||
| 232 | // Priority | |||
| 233 | setText(col_priority_, QString::number(priority_)); | |||
| 234 | ||||
| 235 | // Uplink. | |||
| 236 | setText(col_ul_frames_, QString::number(stats_.UL_frames)); | |||
| 237 | setText(col_ul_bytes_, QString::number(stats_.UL_bytes)); | |||
| 238 | setText(col_ul_mb_s_, QString::number(UL_bw)); | |||
| 239 | setText(col_ul_acks_, QString::number(stats_.UL_acks)); | |||
| 240 | setText(col_ul_nacks_, QString::number(stats_.UL_nacks)); | |||
| 241 | setText(col_ul_missing_, QString::number(stats_.UL_missing)); | |||
| 242 | ||||
| 243 | // Downlink. | |||
| 244 | setText(col_dl_frames_, QString::number(stats_.DL_frames)); | |||
| 245 | setText(col_dl_bytes_, QString::number(stats_.DL_bytes)); | |||
| 246 | setText(col_dl_mb_s_, QString::number(DL_bw)); | |||
| 247 | setText(col_dl_acks_, QString::number(stats_.DL_acks)); | |||
| 248 | setText(col_dl_nacks_, QString::number(stats_.DL_nacks)); | |||
| 249 | setText(col_dl_missing_, QString::number(stats_.DL_missing)); | |||
| 250 | } | |||
| 251 | ||||
| 252 | bool operator< (const QTreeWidgetItem &other) const | |||
| 253 | { | |||
| 254 | if (other.type() != rlc_channel_row_type_) return QTreeWidgetItem::operator< (other); | |||
| 255 | const RlcChannelTreeWidgetItem *other_row = static_cast<const RlcChannelTreeWidgetItem *>(&other); | |||
| 256 | ||||
| 257 | // Switch by selected column. | |||
| 258 | switch (treeWidget()->sortColumn()) { | |||
| 259 | case col_ueid_: | |||
| 260 | // This is channel name. Rank CCCH before SRB before DRB, then channel ID. | |||
| 261 | return channelRank() < other_row->channelRank(); | |||
| 262 | case col_mode_: | |||
| 263 | return mode_ < other_row->mode_; | |||
| 264 | case col_priority_: | |||
| 265 | return priority_ < other_row->priority_; | |||
| 266 | default: | |||
| 267 | break; | |||
| 268 | } | |||
| 269 | ||||
| 270 | return QTreeWidgetItem::operator< (other); | |||
| 271 | } | |||
| 272 | ||||
| 273 | // Filter expression for this bearer. | |||
| 274 | const QString filterExpression(bool showSR, bool showRACH) { | |||
| 275 | // Create an expression to match with all traffic for this UE. | |||
| 276 | QString filter_expr; | |||
| 277 | ||||
| 278 | // Are we taking RLC PDUs from MAC, or not? | |||
| 279 | if (!recent.gui_rlc_use_pdus_from_mac) { | |||
| 280 | if (rat_ == RLC_RAT_LTE0) { | |||
| 281 | filter_expr += QStringLiteral("not mac-lte and ")(QString(QtPrivate::qMakeStringPrivate(u"" "not mac-lte and " ))); | |||
| 282 | } | |||
| 283 | else { | |||
| 284 | filter_expr += QStringLiteral("not mac-nr and ")(QString(QtPrivate::qMakeStringPrivate(u"" "not mac-nr and ") )); | |||
| 285 | } | |||
| 286 | } | |||
| 287 | else { | |||
| 288 | if (rat_ == RLC_RAT_LTE0) { | |||
| 289 | filter_expr += QStringLiteral("mac-lte and ")(QString(QtPrivate::qMakeStringPrivate(u"" "mac-lte and "))); | |||
| 290 | } | |||
| 291 | else { | |||
| 292 | filter_expr += QStringLiteral("mac-nr and ")(QString(QtPrivate::qMakeStringPrivate(u"" "mac-nr and "))); | |||
| 293 | } | |||
| 294 | } | |||
| 295 | ||||
| 296 | if (showSR) { | |||
| 297 | if (rat_ == RLC_RAT_LTE0) { | |||
| 298 | filter_expr += QStringLiteral("(mac-lte.sr-req and mac-lte.ueid == %1) or (")(QString(QtPrivate::qMakeStringPrivate(u"" "(mac-lte.sr-req and mac-lte.ueid == %1) or (" ))).arg(ueid_); | |||
| 299 | } | |||
| 300 | } | |||
| 301 | ||||
| 302 | if (showRACH) { | |||
| 303 | if (rat_ == RLC_RAT_LTE0) { | |||
| 304 | filter_expr += QStringLiteral("(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (")(QString(QtPrivate::qMakeStringPrivate(u"" "(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (" ))).arg(ueid_); | |||
| 305 | } | |||
| 306 | else { | |||
| 307 | filter_expr += QStringLiteral("(mac-nr.rar or ")(QString(QtPrivate::qMakeStringPrivate(u"" "(mac-nr.rar or ") )); | |||
| 308 | } | |||
| 309 | } | |||
| 310 | ||||
| 311 | // Main part of expression. | |||
| 312 | if (rat_ == RLC_RAT_LTE0) { | |||
| 313 | filter_expr += QStringLiteral("rlc-lte.ueid==%1 and rlc-lte.channel-type == %2")(QString(QtPrivate::qMakeStringPrivate(u"" "rlc-lte.ueid==%1 and rlc-lte.channel-type == %2" ))). | |||
| 314 | arg(ueid_).arg(channelType_); | |||
| 315 | } | |||
| 316 | else { | |||
| 317 | filter_expr += QStringLiteral("rlc-nr.ueid==%1 and rlc-nr.bearer-type == %2")(QString(QtPrivate::qMakeStringPrivate(u"" "rlc-nr.ueid==%1 and rlc-nr.bearer-type == %2" ))). | |||
| 318 | arg(ueid_).arg(channelType_); | |||
| 319 | } | |||
| 320 | // Channel/bearer Id | |||
| 321 | if ((channelType_ == CHANNEL_TYPE_SRB4) || (channelType_ == CHANNEL_TYPE_DRB5)) { | |||
| 322 | if (rat_ == RLC_RAT_LTE0) { | |||
| 323 | filter_expr += QStringLiteral(" and rlc-lte.channel-id == %1")(QString(QtPrivate::qMakeStringPrivate(u"" " and rlc-lte.channel-id == %1" ))).arg(channelId_); | |||
| 324 | } | |||
| 325 | else { | |||
| 326 | filter_expr += QStringLiteral(" and rlc-nr.bearer-id == %1")(QString(QtPrivate::qMakeStringPrivate(u"" " and rlc-nr.bearer-id == %1" ))).arg(channelId_); | |||
| 327 | } | |||
| 328 | } | |||
| 329 | ||||
| 330 | // Close () if open because of SR | |||
| 331 | if (showSR) { | |||
| 332 | if (rat_ == RLC_RAT_LTE0) { | |||
| 333 | filter_expr += QStringLiteral(")")(QString(QtPrivate::qMakeStringPrivate(u"" ")"))); | |||
| 334 | } | |||
| 335 | } | |||
| 336 | // Close () if open because of RACH | |||
| 337 | if (showRACH) { | |||
| 338 | if (rat_ == RLC_RAT_LTE0) { | |||
| 339 | filter_expr += QStringLiteral(")")(QString(QtPrivate::qMakeStringPrivate(u"" ")"))); | |||
| 340 | } | |||
| 341 | } | |||
| 342 | ||||
| 343 | return filter_expr; | |||
| 344 | } | |||
| 345 | ||||
| 346 | // Accessors (queried for launching graph) | |||
| 347 | uint8_t get_rat() const { return rat_; } | |||
| 348 | unsigned get_ueid() const { return ueid_; } | |||
| 349 | unsigned get_channelType() const { return channelType_; } | |||
| 350 | unsigned get_channelId() const { return channelId_; } | |||
| 351 | unsigned get_mode() const { return mode_; } | |||
| 352 | ||||
| 353 | bool hasULData() const { return stats_.UL_has_data != 0; } | |||
| 354 | bool hasDLData() const { return stats_.DL_has_data != 0; } | |||
| 355 | ||||
| 356 | QList<QVariant> rowData() const | |||
| 357 | { | |||
| 358 | // Don't output anything for channel entries when exporting to text. | |||
| 359 | return QList<QVariant>(); | |||
| 360 | } | |||
| 361 | ||||
| 362 | private: | |||
| 363 | uint8_t rat_; | |||
| 364 | unsigned ueid_; | |||
| 365 | unsigned channelType_; | |||
| 366 | unsigned channelId_; | |||
| 367 | unsigned mode_; // RLC mode for this bearer | |||
| 368 | unsigned priority_; | |||
| 369 | ||||
| 370 | unsigned channelRank() const | |||
| 371 | { | |||
| 372 | switch (channelType_) { | |||
| 373 | case CHANNEL_TYPE_CCCH1: | |||
| 374 | return 0; | |||
| 375 | case CHANNEL_TYPE_SRB4: | |||
| 376 | return channelId_; | |||
| 377 | case CHANNEL_TYPE_DRB5: | |||
| 378 | return 3 + channelId_; | |||
| 379 | default: | |||
| 380 | // Shouldn't really get here.. | |||
| 381 | return 0; | |||
| 382 | } | |||
| 383 | } | |||
| 384 | ||||
| 385 | rlc_channel_stats stats_; | |||
| 386 | }; | |||
| 387 | ||||
| 388 | ||||
| 389 | // Stats for one UE. TODO: private to class? | |||
| 390 | typedef struct rlc_ue_stats { | |||
| 391 | ||||
| 392 | uint32_t UL_frames; | |||
| 393 | uint32_t UL_total_bytes; | |||
| 394 | nstime_t UL_time_start; | |||
| 395 | nstime_t UL_time_stop; | |||
| 396 | uint32_t UL_total_acks; | |||
| 397 | uint32_t UL_total_nacks; | |||
| 398 | uint32_t UL_total_missing; | |||
| 399 | ||||
| 400 | uint32_t DL_frames; | |||
| 401 | uint32_t DL_total_bytes; | |||
| 402 | nstime_t DL_time_start; | |||
| 403 | nstime_t DL_time_stop; | |||
| 404 | uint32_t DL_total_acks; | |||
| 405 | uint32_t DL_total_nacks; | |||
| 406 | uint32_t DL_total_missing; | |||
| 407 | ||||
| 408 | } rlc_ue_stats; | |||
| 409 | ||||
| 410 | //------------------------------------------------------------------- | |||
| 411 | // UE item. | |||
| 412 | //------------------------------------------------------------------- | |||
| 413 | class RlcUeTreeWidgetItem : public QTreeWidgetItem | |||
| 414 | { | |||
| 415 | public: | |||
| 416 | RlcUeTreeWidgetItem(QTreeWidget *parent, const rlc_3gpp_tap_info *rlt_info) : | |||
| 417 | QTreeWidgetItem (parent, rlc_ue_row_type_), | |||
| 418 | ueid_(0) | |||
| 419 | { | |||
| 420 | ueid_ = rlt_info->ueid; | |||
| 421 | rat_ = rlt_info->rat; | |||
| 422 | ||||
| 423 | // Version matches UE number | |||
| 424 | setText(col_rat_, (rat_ == RLC_RAT_LTE0) ? | |||
| 425 | QObject::tr("LTE") : | |||
| 426 | QObject::tr("NR")); | |||
| 427 | setText(col_ueid_, QString::number(ueid_)); | |||
| 428 | ||||
| 429 | // We create RlcChannelTreeWidgetItems when first data on new channel is seen. | |||
| 430 | // Of course, there will be a channel associated with the PDU | |||
| 431 | // that causes this UE item to be created... | |||
| 432 | memset(&stats_, 0, sizeof(stats_)); | |||
| 433 | CCCH_stats_ = NULL__null; | |||
| 434 | for (int srb=0; srb < 2; srb++) { | |||
| 435 | srb_stats_[srb] = NULL__null; | |||
| 436 | } | |||
| 437 | for (int drb=0; drb < 32; drb++) { | |||
| 438 | drb_stats_[drb] = NULL__null; | |||
| 439 | } | |||
| 440 | } | |||
| 441 | ||||
| 442 | // Does UE match? | |||
| 443 | bool isMatch(const rlc_3gpp_tap_info *rlt_info) | |||
| 444 | { | |||
| 445 | return (rat_ == rlt_info->rat) && (ueid_ == rlt_info->ueid); | |||
| 446 | } | |||
| 447 | ||||
| 448 | // Update UE/channels from tap info. | |||
| 449 | void update(const rlc_3gpp_tap_info *tap_info) | |||
| 450 | { | |||
| 451 | // TODO: update title with number of UEs and frames like MAC does? | |||
| 452 | ||||
| 453 | // N.B. not really expecting to see common stats - ignoring them. | |||
| 454 | switch (tap_info->channelType) { | |||
| 455 | case CHANNEL_TYPE_BCCH_BCH2: | |||
| 456 | case CHANNEL_TYPE_BCCH_DL_SCH6: | |||
| 457 | case CHANNEL_TYPE_PCCH3: | |||
| 458 | return; | |||
| 459 | ||||
| 460 | default: | |||
| 461 | // Drop through for UE-specific. | |||
| 462 | break; | |||
| 463 | } | |||
| 464 | ||||
| 465 | // UE-level traffic stats. | |||
| 466 | if (tap_info->direction == DIRECTION_UPLINK0) { | |||
| 467 | // Update time range. | |||
| 468 | if (stats_.UL_frames == 0) { | |||
| 469 | stats_.UL_time_start = tap_info->rlc_time; | |||
| 470 | } | |||
| 471 | stats_.UL_time_stop = tap_info->rlc_time; | |||
| 472 | ||||
| 473 | stats_.UL_frames++; | |||
| 474 | stats_.UL_total_bytes += tap_info->pduLength; | |||
| 475 | ||||
| 476 | // Status PDU counters. | |||
| 477 | if (tap_info->isControlPDU) { | |||
| 478 | stats_.UL_total_acks++; | |||
| 479 | stats_.UL_total_nacks += tap_info->noOfNACKs; | |||
| 480 | } | |||
| 481 | ||||
| 482 | stats_.UL_total_missing += tap_info->missingSNs; | |||
| 483 | } | |||
| 484 | else { | |||
| 485 | // Update time range. | |||
| 486 | if (stats_.DL_frames == 0) { | |||
| 487 | stats_.DL_time_start = tap_info->rlc_time; | |||
| 488 | } | |||
| 489 | stats_.DL_time_stop = tap_info->rlc_time; | |||
| 490 | ||||
| 491 | stats_.DL_frames++; | |||
| 492 | stats_.DL_total_bytes += tap_info->pduLength; | |||
| 493 | ||||
| 494 | // Status PDU counters. | |||
| 495 | if (tap_info->isControlPDU) { | |||
| 496 | stats_.DL_total_acks++; | |||
| 497 | stats_.DL_total_nacks += tap_info->noOfNACKs; | |||
| 498 | } | |||
| 499 | ||||
| 500 | stats_.DL_total_missing += tap_info->missingSNs; | |||
| 501 | } | |||
| 502 | ||||
| 503 | RlcChannelTreeWidgetItem *channel_item; | |||
| 504 | ||||
| 505 | // Find or create tree item for this channel. | |||
| 506 | switch (tap_info->channelType) { | |||
| 507 | case CHANNEL_TYPE_CCCH1: | |||
| 508 | channel_item = CCCH_stats_; | |||
| 509 | if (channel_item == NULL__null) { | |||
| 510 | channel_item = CCCH_stats_ = | |||
| 511 | new RlcChannelTreeWidgetItem(this, tap_info->rat, tap_info->ueid, RLC_TM_MODE1, | |||
| 512 | tap_info->channelType, tap_info->channelId); | |||
| 513 | } | |||
| 514 | break; | |||
| 515 | ||||
| 516 | case CHANNEL_TYPE_SRB4: | |||
| 517 | channel_item = srb_stats_[tap_info->channelId-1]; | |||
| 518 | if (channel_item == NULL__null) { | |||
| 519 | channel_item = srb_stats_[tap_info->channelId-1] = | |||
| 520 | new RlcChannelTreeWidgetItem(this, tap_info->rat, tap_info->ueid, RLC_AM_MODE4, | |||
| 521 | tap_info->channelType, tap_info->channelId); | |||
| 522 | } | |||
| 523 | break; | |||
| 524 | ||||
| 525 | case CHANNEL_TYPE_DRB5: | |||
| 526 | channel_item = drb_stats_[tap_info->channelId-1]; | |||
| 527 | if (channel_item == NULL__null) { | |||
| 528 | channel_item = drb_stats_[tap_info->channelId-1] = | |||
| 529 | new RlcChannelTreeWidgetItem(this, tap_info->rat, tap_info->ueid, tap_info->rlcMode, | |||
| 530 | tap_info->channelType, tap_info->channelId); | |||
| 531 | } | |||
| 532 | break; | |||
| 533 | ||||
| 534 | default: | |||
| 535 | // Shouldn't get here... | |||
| 536 | return; | |||
| 537 | } | |||
| 538 | ||||
| 539 | // Update channel with tap_info. | |||
| 540 | channel_item->update(tap_info); | |||
| 541 | } | |||
| 542 | ||||
| 543 | // Draw UE entry | |||
| 544 | void draw() | |||
| 545 | { | |||
| 546 | // Fixed fields only drawn once from constructor so don't redraw here. | |||
| 547 | ||||
| 548 | /* Calculate bandwidths. */ | |||
| 549 | double UL_bw = calculate_bw(&stats_.UL_time_start, | |||
| 550 | &stats_.UL_time_stop, | |||
| 551 | stats_.UL_total_bytes); | |||
| 552 | double DL_bw = calculate_bw(&stats_.DL_time_start, | |||
| 553 | &stats_.DL_time_stop, | |||
| 554 | stats_.DL_total_bytes); | |||
| 555 | ||||
| 556 | setText(col_rat_, (rat_ == RLC_RAT_LTE0) ? QStringLiteral("LTE")(QString(QtPrivate::qMakeStringPrivate(u"" "LTE"))) : QStringLiteral("NR")(QString(QtPrivate::qMakeStringPrivate(u"" "NR")))); | |||
| 557 | ||||
| 558 | // Uplink. | |||
| 559 | setText(col_ul_frames_, QString::number(stats_.UL_frames)); | |||
| 560 | setText(col_ul_bytes_, QString::number(stats_.UL_total_bytes)); | |||
| 561 | setText(col_ul_mb_s_, QString::number(UL_bw)); | |||
| 562 | setText(col_ul_acks_, QString::number(stats_.UL_total_acks)); | |||
| 563 | setText(col_ul_nacks_, QString::number(stats_.UL_total_nacks)); | |||
| 564 | setText(col_ul_missing_, QString::number(stats_.UL_total_missing)); | |||
| 565 | ||||
| 566 | // Downlink. | |||
| 567 | setText(col_dl_frames_, QString::number(stats_.DL_frames)); | |||
| 568 | setText(col_dl_bytes_, QString::number(stats_.DL_total_bytes)); | |||
| 569 | setText(col_dl_mb_s_, QString::number(DL_bw)); | |||
| 570 | setText(col_dl_acks_, QString::number(stats_.DL_total_acks)); | |||
| 571 | setText(col_dl_nacks_, QString::number(stats_.DL_total_nacks)); | |||
| 572 | setText(col_dl_missing_, QString::number(stats_.DL_total_missing)); | |||
| 573 | ||||
| 574 | // Call draw() for each channel for this UE. | |||
| 575 | if (CCCH_stats_ != NULL__null) { | |||
| 576 | CCCH_stats_->draw(); | |||
| 577 | } | |||
| 578 | for (int srb=0; srb < 2; srb++) { | |||
| 579 | if (srb_stats_[srb] != NULL__null) { | |||
| 580 | srb_stats_[srb]->draw(); | |||
| 581 | } | |||
| 582 | } | |||
| 583 | for (int drb=0; drb < 32; drb++) { | |||
| 584 | if (drb_stats_[drb] != NULL__null) { | |||
| 585 | drb_stats_[drb]->draw(); | |||
| 586 | } | |||
| 587 | } | |||
| 588 | } | |||
| 589 | ||||
| 590 | bool operator< (const QTreeWidgetItem &other) const | |||
| 591 | { | |||
| 592 | if (other.type() != rlc_ue_row_type_) return QTreeWidgetItem::operator< (other); | |||
| 593 | const RlcUeTreeWidgetItem *other_row = static_cast<const RlcUeTreeWidgetItem *>(&other); | |||
| 594 | ||||
| 595 | switch (treeWidget()->sortColumn()) { | |||
| 596 | case col_ueid_: | |||
| 597 | return ueid_ < other_row->ueid_; | |||
| 598 | default: | |||
| 599 | break; | |||
| 600 | } | |||
| 601 | ||||
| 602 | return QTreeWidgetItem::operator< (other); | |||
| 603 | } | |||
| 604 | ||||
| 605 | // Filter expression for UE. | |||
| 606 | const QString filterExpression(bool showSR, bool showRACH) | |||
| 607 | { | |||
| 608 | // Create an expression to match with all traffic for this UE. | |||
| 609 | QString filter_expr; | |||
| 610 | ||||
| 611 | // Are we taking RLC PDUs from MAC, or not? | |||
| 612 | if (!recent.gui_rlc_use_pdus_from_mac) { | |||
| 613 | if (rat_ == RLC_RAT_LTE0) { | |||
| 614 | filter_expr += QStringLiteral("not mac-lte and ")(QString(QtPrivate::qMakeStringPrivate(u"" "not mac-lte and " ))); | |||
| 615 | } | |||
| 616 | else { | |||
| 617 | filter_expr += QStringLiteral("not mac-nr and ")(QString(QtPrivate::qMakeStringPrivate(u"" "not mac-nr and ") )); | |||
| 618 | } | |||
| 619 | } | |||
| 620 | else { | |||
| 621 | if (rat_ == RLC_RAT_LTE0) { | |||
| 622 | filter_expr += QStringLiteral("mac-lte and ")(QString(QtPrivate::qMakeStringPrivate(u"" "mac-lte and "))); | |||
| 623 | } | |||
| 624 | else { | |||
| 625 | filter_expr += QStringLiteral("mac-nr and ")(QString(QtPrivate::qMakeStringPrivate(u"" "mac-nr and "))); | |||
| 626 | } | |||
| 627 | } | |||
| 628 | ||||
| 629 | if (showSR) { | |||
| 630 | if (rat_ == RLC_RAT_LTE0) { | |||
| 631 | filter_expr += QStringLiteral("(mac-lte.sr-req and mac-lte.ueid == %1) or (")(QString(QtPrivate::qMakeStringPrivate(u"" "(mac-lte.sr-req and mac-lte.ueid == %1) or (" ))).arg(ueid_); | |||
| 632 | } | |||
| 633 | } | |||
| 634 | ||||
| 635 | if (showRACH) { | |||
| 636 | if (rat_ == RLC_RAT_LTE0) { | |||
| 637 | filter_expr += QStringLiteral("(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (")(QString(QtPrivate::qMakeStringPrivate(u"" "(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (" ))).arg(ueid_); | |||
| 638 | } | |||
| 639 | else { | |||
| 640 | filter_expr += QStringLiteral("mac-nr.rar or ")(QString(QtPrivate::qMakeStringPrivate(u"" "mac-nr.rar or ")) ); | |||
| 641 | } | |||
| 642 | } | |||
| 643 | ||||
| 644 | // Must match UE | |||
| 645 | if (rat_ == RLC_RAT_LTE0) { | |||
| 646 | filter_expr += QStringLiteral("rlc-lte.ueid==%1")(QString(QtPrivate::qMakeStringPrivate(u"" "rlc-lte.ueid==%1" ))).arg(ueid_); | |||
| 647 | } | |||
| 648 | else { | |||
| 649 | filter_expr += QStringLiteral("rlc-nr.ueid==%1")(QString(QtPrivate::qMakeStringPrivate(u"" "rlc-nr.ueid==%1") )).arg(ueid_); | |||
| 650 | } | |||
| 651 | ||||
| 652 | // Close () if open because of SR | |||
| 653 | if (showSR) { | |||
| 654 | if (rat_ == RLC_RAT_LTE0) { | |||
| 655 | filter_expr += QStringLiteral(")")(QString(QtPrivate::qMakeStringPrivate(u"" ")"))); | |||
| 656 | } | |||
| 657 | } | |||
| 658 | // Close () if open because of RACH | |||
| 659 | if (showRACH) { | |||
| 660 | if (rat_ == RLC_RAT_LTE0) { | |||
| 661 | filter_expr += QStringLiteral(")")(QString(QtPrivate::qMakeStringPrivate(u"" ")"))); | |||
| 662 | } | |||
| 663 | } | |||
| 664 | ||||
| 665 | return filter_expr; | |||
| 666 | } | |||
| 667 | ||||
| 668 | QList<QVariant> rowData() const | |||
| 669 | { | |||
| 670 | QList<QVariant> row_data; | |||
| 671 | ||||
| 672 | // Key fields. | |||
| 673 | // After the UEId field, there are 2 unused columns for UE entries. | |||
| 674 | row_data << ueid_ << QString("") << QString(""); | |||
| 675 | ||||
| 676 | // UL | |||
| 677 | row_data << stats_.UL_frames << stats_.UL_total_bytes | |||
| 678 | << calculate_bw(&stats_.UL_time_start, | |||
| 679 | &stats_.UL_time_stop, | |||
| 680 | stats_.UL_total_bytes) | |||
| 681 | << stats_.UL_total_acks << stats_.UL_total_nacks << stats_.UL_total_missing; | |||
| 682 | ||||
| 683 | // DL | |||
| 684 | row_data << stats_.DL_frames << stats_.DL_total_bytes | |||
| 685 | << calculate_bw(&stats_.DL_time_start, | |||
| 686 | &stats_.DL_time_stop, | |||
| 687 | stats_.DL_total_bytes) | |||
| 688 | << stats_.DL_total_acks << stats_.DL_total_nacks << stats_.DL_total_missing; | |||
| 689 | return row_data; | |||
| 690 | } | |||
| 691 | ||||
| 692 | private: | |||
| 693 | uint8_t rat_; | |||
| 694 | unsigned ueid_; | |||
| 695 | rlc_ue_stats stats_; | |||
| 696 | ||||
| 697 | // Channel counters stored in channel sub-items. | |||
| 698 | RlcChannelTreeWidgetItem* CCCH_stats_; | |||
| 699 | RlcChannelTreeWidgetItem* srb_stats_[2]; | |||
| 700 | RlcChannelTreeWidgetItem* drb_stats_[32]; | |||
| 701 | }; | |||
| 702 | ||||
| 703 | ||||
| 704 | // Only the first 4 columns headings differ between UE and channel rows. | |||
| 705 | static const QString ue_col_0_title_ = QObject::tr("RAT"); | |||
| 706 | static const QString ue_col_1_title_ = QObject::tr("UE Id"); | |||
| 707 | static const QString ue_col_2_title_ = QObject::tr(""); | |||
| 708 | static const QString ue_col_3_title_ = QObject::tr(""); | |||
| 709 | ||||
| 710 | static const QString channel_col_0_title_ = QObject::tr(""); | |||
| 711 | static const QString channel_col_1_title_ = QObject::tr("Name"); | |||
| 712 | static const QString channel_col_2_title_ = QObject::tr("Mode"); | |||
| 713 | static const QString channel_col_3_title_ = QObject::tr("Priority"); | |||
| 714 | ||||
| 715 | ||||
| 716 | ||||
| 717 | //------------------------------------------------------------------------------------------ | |||
| 718 | // Dialog | |||
| 719 | ||||
| 720 | // Constructor. | |||
| 721 | LteRlcStatisticsDialog::LteRlcStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter) : | |||
| 722 | TapParameterDialog(parent, cf, HELP_STATS_LTE_RLC_TRAFFIC_DIALOG), | |||
| 723 | cf_(cf), | |||
| 724 | packet_count_(0) | |||
| 725 | { | |||
| 726 | setWindowSubtitle(tr("3GPP RLC Statistics")); | |||
| 727 | loadGeometry((parent.width() * 5) / 5, (parent.height() * 3) / 4, "LTERLCStatisticsDialog"); | |||
| 728 | ||||
| 729 | // Create a grid for filtering-related widgetsto also appear in layout. | |||
| 730 | int filter_controls_layout_idx = verticalLayout()->indexOf(filterLayout()->widget()); | |||
| 731 | QGridLayout *filter_controls_grid = new QGridLayout(); | |||
| 732 | // Insert into the vertical layout | |||
| 733 | verticalLayout()->insertLayout(filter_controls_layout_idx, filter_controls_grid); | |||
| 734 | int one_em = fontMetrics().height(); | |||
| 735 | filter_controls_grid->setColumnMinimumWidth(2, one_em * 2); | |||
| 736 | filter_controls_grid->setColumnStretch(2, 1); | |||
| 737 | filter_controls_grid->setColumnMinimumWidth(5, one_em * 2); | |||
| 738 | filter_controls_grid->setColumnStretch(5, 1); | |||
| 739 | ||||
| 740 | // Add individual controls into the grid | |||
| 741 | launchULGraph_ = new QPushButton(tr("Launch UL Graph")); | |||
| 742 | launchULGraph_->setEnabled(false); | |||
| 743 | filter_controls_grid->addWidget(launchULGraph_); | |||
| 744 | connect(launchULGraph_, SIGNAL(clicked())qFlagLocation("2" "clicked()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp" ":" "744"), this, SLOT(launchULGraphButtonClicked())qFlagLocation("1" "launchULGraphButtonClicked()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp" ":" "744")); | |||
| 745 | launchDLGraph_ = new QPushButton(tr("Launch DL Graph")); | |||
| 746 | launchDLGraph_->setEnabled(false); | |||
| 747 | filter_controls_grid->addWidget(launchDLGraph_); | |||
| 748 | connect(launchDLGraph_, SIGNAL(clicked())qFlagLocation("2" "clicked()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp" ":" "748"), this, SLOT(launchDLGraphButtonClicked())qFlagLocation("1" "launchDLGraphButtonClicked()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp" ":" "748")); | |||
| 749 | ||||
| 750 | showSRFilterCheckBox_ = new QCheckBox(tr("Include SR frames in filter")); | |||
| 751 | filter_controls_grid->addWidget(showSRFilterCheckBox_); | |||
| 752 | showRACHFilterCheckBox_ = new QCheckBox(tr("Include RACH frames in filter")); | |||
| 753 | filter_controls_grid->addWidget(showRACHFilterCheckBox_); | |||
| 754 | ||||
| 755 | useRLCFramesFromMacCheckBox_ = new QCheckBox(tr("Use RLC frames only from MAC frames")); | |||
| 756 | useRLCFramesFromMacCheckBox_->setCheckState(recent.gui_rlc_use_pdus_from_mac ? | |||
| 757 | Qt::Checked : | |||
| 758 | Qt::Unchecked); | |||
| 759 | connect(useRLCFramesFromMacCheckBox_, SIGNAL(clicked(bool))qFlagLocation("2" "clicked(bool)" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp" ":" "759"), this, | |||
| 760 | SLOT(useRLCFramesFromMacCheckBoxToggled(bool))qFlagLocation("1" "useRLCFramesFromMacCheckBoxToggled(bool)" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp" ":" "760")); | |||
| 761 | filter_controls_grid->addWidget(useRLCFramesFromMacCheckBox_); | |||
| 762 | ||||
| 763 | QStringList header_labels = QStringList() | |||
| 764 | << "" << "" << "" << "" | |||
| 765 | << tr("UL Frames") << tr("UL Bytes") << tr("UL MB/s") | |||
| 766 | << tr("UL ACKs") << tr("UL NACKs") << tr("UL Missing") | |||
| 767 | << tr("DL Frames") << tr("DL Bytes") << tr("DL MB/s") | |||
| 768 | << tr("DL ACKs") << tr("DL NACKs") << tr("DL Missing"); | |||
| 769 | statsTreeWidget()->setHeaderLabels(header_labels); | |||
| 770 | updateHeaderLabels(); | |||
| 771 | ||||
| 772 | statsTreeWidget()->sortByColumn(col_ueid_, Qt::AscendingOrder); | |||
| 773 | ||||
| 774 | // resizeColumnToContents doesn't work well here, so set sizes manually. | |||
| 775 | for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) { | |||
| 776 | switch (col) { | |||
| 777 | case col_rat_: | |||
| 778 | statsTreeWidget()->setColumnWidth(col, one_em * 3); | |||
| 779 | break; | |||
| 780 | case col_ueid_: | |||
| 781 | statsTreeWidget()->setColumnWidth(col, one_em * 7); | |||
| 782 | break; | |||
| 783 | case col_ul_frames_: | |||
| 784 | case col_dl_frames_: | |||
| 785 | statsTreeWidget()->setColumnWidth(col, one_em * 5); | |||
| 786 | break; | |||
| 787 | case col_ul_acks_: | |||
| 788 | case col_dl_acks_: | |||
| 789 | statsTreeWidget()->setColumnWidth(col, one_em * 5); | |||
| 790 | break; | |||
| 791 | case col_ul_nacks_: | |||
| 792 | case col_dl_nacks_: | |||
| 793 | statsTreeWidget()->setColumnWidth(col, one_em * 6); | |||
| 794 | break; | |||
| 795 | case col_ul_missing_: | |||
| 796 | case col_dl_missing_: | |||
| 797 | statsTreeWidget()->setColumnWidth(col, one_em * 7); | |||
| 798 | break; | |||
| 799 | case col_ul_mb_s_: | |||
| 800 | case col_dl_mb_s_: | |||
| 801 | statsTreeWidget()->setColumnWidth(col, one_em * 6); | |||
| 802 | break; | |||
| 803 | ||||
| 804 | default: | |||
| 805 | // The rest are numeric. | |||
| 806 | statsTreeWidget()->setColumnWidth(col, one_em * 4); | |||
| 807 | break; | |||
| 808 | } | |||
| 809 | } | |||
| 810 | ||||
| 811 | addFilterActions(); | |||
| 812 | ||||
| 813 | if (filter) { | |||
| 814 | setDisplayFilter(filter); | |||
| 815 | } | |||
| 816 | ||||
| 817 | // Set handler for when the tree item changes to set the appropriate labels. | |||
| 818 | connect(statsTreeWidget(), SIGNAL(itemSelectionChanged())qFlagLocation("2" "itemSelectionChanged()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp" ":" "818"), | |||
| 819 | this, SLOT(updateItemSelectionChanged())qFlagLocation("1" "updateItemSelectionChanged()" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp" ":" "819")); | |||
| 820 | ||||
| 821 | // Set handler for when display filter string is changed. | |||
| 822 | connect(this, SIGNAL(updateFilter(QString))qFlagLocation("2" "updateFilter(QString)" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp" ":" "822"), | |||
| 823 | this, SLOT(filterUpdated(QString))qFlagLocation("1" "filterUpdated(QString)" "\0" "ui/qt/lte_rlc_statistics_dialog.cpp" ":" "823")); | |||
| 824 | } | |||
| 825 | ||||
| 826 | // Destructor. | |||
| 827 | LteRlcStatisticsDialog::~LteRlcStatisticsDialog() | |||
| 828 | { | |||
| 829 | } | |||
| 830 | ||||
| 831 | void LteRlcStatisticsDialog::tapReset(void *ws_dlg_ptr) | |||
| 832 | { | |||
| 833 | LteRlcStatisticsDialog *ws_dlg = static_cast<LteRlcStatisticsDialog *>(ws_dlg_ptr); | |||
| 834 | if (!ws_dlg) { | |||
| 835 | return; | |||
| 836 | } | |||
| 837 | ||||
| 838 | // Clears/deletes all UEs. | |||
| 839 | ws_dlg->statsTreeWidget()->clear(); | |||
| 840 | ws_dlg->packet_count_ = 0; | |||
| 841 | } | |||
| 842 | ||||
| 843 | // Process the tap info from a dissected RLC PDU. | |||
| 844 | // Returns TAP_PACKET_REDRAW if a redraw is needed, TAP_PACKET_DONT_REDRAW otherwise. | |||
| 845 | tap_packet_status LteRlcStatisticsDialog::tapPacket(void *ws_dlg_ptr, struct _packet_info *, epan_dissect *, const void *rlc_3gpp_tap_info_ptr, tap_flags_t) | |||
| 846 | { | |||
| 847 | // Look up dialog. | |||
| 848 | LteRlcStatisticsDialog *ws_dlg = static_cast<LteRlcStatisticsDialog *>(ws_dlg_ptr); | |||
| 849 | const rlc_3gpp_tap_info *rlt_info = (const rlc_3gpp_tap_info *) rlc_3gpp_tap_info_ptr; | |||
| 850 | if (!ws_dlg || !rlt_info) { | |||
| ||||
| 851 | return TAP_PACKET_DONT_REDRAW; | |||
| 852 | } | |||
| 853 | ||||
| 854 | // Are we ignoring RLC frames that were found in MAC frames, or only those | |||
| 855 | // that were logged separately? | |||
| 856 | if ((!recent.gui_rlc_use_pdus_from_mac && rlt_info->loggedInMACFrame) || | |||
| 857 | (recent.gui_rlc_use_pdus_from_mac
| |||
| 858 | return TAP_PACKET_DONT_REDRAW; | |||
| 859 | } | |||
| 860 | ||||
| 861 | ws_dlg->incFrameCount(); | |||
| 862 | ||||
| 863 | // Look for this UE (TODO: avoid linear search if have lots of UEs in capture?) | |||
| 864 | RlcUeTreeWidgetItem *ue_ti = NULL__null; | |||
| 865 | for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) { | |||
| 866 | QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i); | |||
| 867 | if (ti->type() != rlc_ue_row_type_) continue; | |||
| 868 | // Get UE object for this entry | |||
| 869 | RlcUeTreeWidgetItem *cur_ru_ti = static_cast<RlcUeTreeWidgetItem*>(ti); | |||
| 870 | ||||
| 871 | // Does it match this tap? | |||
| 872 | if (cur_ru_ti->isMatch(rlt_info)) { | |||
| 873 | ue_ti = cur_ru_ti; | |||
| 874 | break; | |||
| 875 | } | |||
| 876 | } | |||
| 877 | ||||
| 878 | if (!ue_ti
| |||
| 879 | // Existing UE wasn't found so create a new one. | |||
| 880 | ue_ti = new RlcUeTreeWidgetItem(ws_dlg->statsTreeWidget(), rlt_info); | |||
| 881 | for (int col = 0; col < ws_dlg->statsTreeWidget()->columnCount(); col++) { | |||
| 882 | // int QTreeWidgetItem::textAlignment(int column) const | |||
| 883 | // Returns the text alignment for the label in the given column. | |||
| 884 | // Note: This function returns an int for historical reasons. It will be corrected to return Qt::Alignment in Qt 7. | |||
| 885 | ue_ti->setTextAlignment(col, static_cast<Qt::Alignment>(ws_dlg->statsTreeWidget()->headerItem()->textAlignment(col))); | |||
| 886 | } | |||
| 887 | } | |||
| 888 | ||||
| 889 | // Update the UE from the information in the tap structure. | |||
| 890 | ue_ti->update(rlt_info); | |||
| 891 | ||||
| 892 | return TAP_PACKET_REDRAW; | |||
| ||||
| 893 | } | |||
| 894 | ||||
| 895 | void LteRlcStatisticsDialog::tapDraw(void *ws_dlg_ptr) | |||
| 896 | { | |||
| 897 | // Look up UE. | |||
| 898 | LteRlcStatisticsDialog *ws_dlg = static_cast<LteRlcStatisticsDialog *>(ws_dlg_ptr); | |||
| 899 | if (!ws_dlg) { | |||
| 900 | return; | |||
| 901 | } | |||
| 902 | ||||
| 903 | // Draw each UE. | |||
| 904 | for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) { | |||
| 905 | QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i); | |||
| 906 | if (ti->type() != rlc_ue_row_type_) continue; | |||
| 907 | ||||
| 908 | RlcUeTreeWidgetItem *ru_ti = static_cast<RlcUeTreeWidgetItem*>(ti); | |||
| 909 | ru_ti->draw(); | |||
| 910 | } | |||
| 911 | ||||
| 912 | // Update title | |||
| 913 | ws_dlg->setWindowSubtitle(tr("LTE RLC Statistics (%1 UEs, %2 frames)"). | |||
| 914 | arg(ws_dlg->statsTreeWidget()->topLevelItemCount()).arg(ws_dlg->getFrameCount())); | |||
| 915 | } | |||
| 916 | ||||
| 917 | void LteRlcStatisticsDialog::useRLCFramesFromMacCheckBoxToggled(bool state) | |||
| 918 | { | |||
| 919 | // Update state to be stored in recent preferences | |||
| 920 | recent.gui_rlc_use_pdus_from_mac = state; | |||
| 921 | ||||
| 922 | // Retap to get updated list of PDUs | |||
| 923 | fillTree(); | |||
| 924 | } | |||
| 925 | ||||
| 926 | // Return a filter expression for currently selected UE or bearer row | |||
| 927 | const QString LteRlcStatisticsDialog::filterExpression() | |||
| 928 | { | |||
| 929 | QString filter_expr; | |||
| 930 | if (statsTreeWidget()->selectedItems().count() > 0) { | |||
| 931 | QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; | |||
| 932 | ||||
| 933 | // Generate expression according to what type of item is selected. | |||
| 934 | if (ti->type() == rlc_ue_row_type_) { | |||
| 935 | RlcUeTreeWidgetItem *ru_ti = static_cast<RlcUeTreeWidgetItem*>(ti); | |||
| 936 | filter_expr = ru_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked, | |||
| 937 | showRACHFilterCheckBox_->checkState() > Qt::Unchecked); | |||
| 938 | } else if (ti->type() == rlc_channel_row_type_) { | |||
| 939 | RlcChannelTreeWidgetItem *rc_ti = static_cast<RlcChannelTreeWidgetItem*>(ti); | |||
| 940 | filter_expr = rc_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked, | |||
| 941 | showRACHFilterCheckBox_->checkState() > Qt::Unchecked); | |||
| 942 | } | |||
| 943 | } | |||
| 944 | return filter_expr; | |||
| 945 | } | |||
| 946 | ||||
| 947 | void LteRlcStatisticsDialog::fillTree() | |||
| 948 | { | |||
| 949 | if (!registerTapListener("rlc-3gpp", | |||
| 950 | this, | |||
| 951 | displayFilter_.toLatin1().data(), | |||
| 952 | TL_REQUIRES_NOTHING0x00000000, | |||
| 953 | tapReset, | |||
| 954 | tapPacket, | |||
| 955 | tapDraw)) { | |||
| 956 | reject(); | |||
| 957 | return; | |||
| 958 | } | |||
| 959 | ||||
| 960 | cap_file_.retapPackets(); | |||
| 961 | tapDraw(this); | |||
| 962 | removeTapListeners(); | |||
| 963 | ||||
| 964 | } | |||
| 965 | ||||
| 966 | void LteRlcStatisticsDialog::updateItemSelectionChanged() | |||
| 967 | { | |||
| 968 | updateHeaderLabels(); | |||
| 969 | ||||
| 970 | bool enableULGraphButton = false, enableDLGraphButton = false; | |||
| 971 | if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) { | |||
| 972 | QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; | |||
| 973 | RlcChannelTreeWidgetItem *rc_ti = static_cast<RlcChannelTreeWidgetItem*>(ti); | |||
| 974 | enableULGraphButton = rc_ti->hasULData(); | |||
| 975 | enableDLGraphButton = rc_ti->hasDLData(); | |||
| 976 | } | |||
| 977 | ||||
| 978 | // Only enabling graph buttons for channel entries. | |||
| 979 | launchULGraph_->setEnabled(enableULGraphButton); | |||
| 980 | launchDLGraph_->setEnabled(enableDLGraphButton); | |||
| 981 | } | |||
| 982 | ||||
| 983 | void LteRlcStatisticsDialog::updateHeaderLabels() | |||
| 984 | { | |||
| 985 | if (statsTreeWidget()->selectedItems().count() > 0 && | |||
| 986 | statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) { | |||
| 987 | ||||
| 988 | // UE column headings. | |||
| 989 | statsTreeWidget()->headerItem()->setText(col_rat_, channel_col_0_title_); | |||
| 990 | statsTreeWidget()->headerItem()->setText(col_ueid_, channel_col_1_title_); | |||
| 991 | statsTreeWidget()->headerItem()->setText(col_mode_, channel_col_2_title_); | |||
| 992 | statsTreeWidget()->headerItem()->setText(col_priority_, channel_col_3_title_); | |||
| 993 | } else { | |||
| 994 | // Channel column headings. | |||
| 995 | statsTreeWidget()->headerItem()->setText(col_rat_, ue_col_0_title_); | |||
| 996 | statsTreeWidget()->headerItem()->setText(col_ueid_, ue_col_1_title_); | |||
| 997 | statsTreeWidget()->headerItem()->setText(col_mode_, ue_col_2_title_); | |||
| 998 | statsTreeWidget()->headerItem()->setText(col_priority_, ue_col_3_title_); | |||
| 999 | } | |||
| 1000 | } | |||
| 1001 | ||||
| 1002 | void LteRlcStatisticsDialog::captureFileClosing() | |||
| 1003 | { | |||
| 1004 | remove_tap_listener(this); | |||
| 1005 | ||||
| 1006 | WiresharkDialog::captureFileClosing(); | |||
| 1007 | } | |||
| 1008 | ||||
| 1009 | // Launch a UL graph for the currently-selected channel. | |||
| 1010 | void LteRlcStatisticsDialog::launchULGraphButtonClicked() | |||
| 1011 | { | |||
| 1012 | if (statsTreeWidget()->selectedItems().count() > 0 && | |||
| 1013 | statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) { | |||
| 1014 | ||||
| 1015 | // Get the channel item. | |||
| 1016 | QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; | |||
| 1017 | RlcChannelTreeWidgetItem *rc_ti = static_cast<RlcChannelTreeWidgetItem*>(ti); | |||
| 1018 | // Launch the graph. | |||
| 1019 | emit launchRLCGraph(true, | |||
| 1020 | rc_ti->get_rat(), | |||
| 1021 | rc_ti->get_ueid(), | |||
| 1022 | rc_ti->get_mode(), | |||
| 1023 | rc_ti->get_channelType(), | |||
| 1024 | rc_ti->get_channelId(), | |||
| 1025 | DIRECTION_UPLINK0); | |||
| 1026 | } | |||
| 1027 | } | |||
| 1028 | ||||
| 1029 | // Launch a DL graph for the currently-selected channel. | |||
| 1030 | void LteRlcStatisticsDialog::launchDLGraphButtonClicked() | |||
| 1031 | { | |||
| 1032 | if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) { | |||
| 1033 | // Get the channel item. | |||
| 1034 | QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; | |||
| 1035 | RlcChannelTreeWidgetItem *rc_ti = static_cast<RlcChannelTreeWidgetItem*>(ti); | |||
| 1036 | emit launchRLCGraph(true, | |||
| 1037 | rc_ti->get_rat(), | |||
| 1038 | rc_ti->get_ueid(), | |||
| 1039 | rc_ti->get_mode(), | |||
| 1040 | rc_ti->get_channelType(), | |||
| 1041 | rc_ti->get_channelId(), | |||
| 1042 | DIRECTION_DOWNLINK1); | |||
| 1043 | } | |||
| 1044 | } | |||
| 1045 | ||||
| 1046 | ||||
| 1047 | // Store filter from signal. | |||
| 1048 | void LteRlcStatisticsDialog::filterUpdated(QString filter) | |||
| 1049 | { | |||
| 1050 | displayFilter_ = filter; | |||
| 1051 | } | |||
| 1052 | ||||
| 1053 | // Get the item for the row, depending upon the type of tree item. | |||
| 1054 | QList<QVariant> LteRlcStatisticsDialog::treeItemData(QTreeWidgetItem *item) const | |||
| 1055 | { | |||
| 1056 | // Cast up to our type. | |||
| 1057 | RlcChannelTreeWidgetItem *channel_item = dynamic_cast<RlcChannelTreeWidgetItem*>(item); | |||
| 1058 | if (channel_item) { | |||
| 1059 | return channel_item->rowData(); | |||
| 1060 | } | |||
| 1061 | RlcUeTreeWidgetItem *ue_item = dynamic_cast<RlcUeTreeWidgetItem*>(item); | |||
| 1062 | if (ue_item) { | |||
| 1063 | return ue_item->rowData(); | |||
| 1064 | } | |||
| 1065 | ||||
| 1066 | // Need to return something.. | |||
| 1067 | return QList<QVariant>(); | |||
| 1068 | } | |||
| 1069 | ||||
| 1070 | ||||
| 1071 | // Stat command + args | |||
| 1072 | ||||
| 1073 | static bool | |||
| 1074 | lte_rlc_statistics_init(const char *args, void*) | |||
| 1075 | { | |||
| 1076 | QStringList args_l = QString(args).split(','); | |||
| 1077 | QByteArray filter; | |||
| 1078 | if (args_l.length() > 2) { | |||
| 1079 | filter = QStringList(args_l.mid(2)).join(",").toUtf8(); | |||
| 1080 | } | |||
| 1081 | mainApp->emitStatCommandSignal("LteRlcStatistics", filter.constData(), NULL__null); | |||
| 1082 | return true; | |||
| 1083 | } | |||
| 1084 | ||||
| 1085 | static stat_tap_ui lte_rlc_statistics_ui = { | |||
| 1086 | REGISTER_TELEPHONY_GROUP_3GPP_UU, | |||
| 1087 | QT_TRANSLATE_NOOP("LteRlcStatisticsDialog", "RLC Statistics")"RLC Statistics", | |||
| 1088 | "rlc-3gpp,stat", // cli_string | |||
| 1089 | lte_rlc_statistics_init, | |||
| 1090 | 0, // nparams | |||
| 1091 | NULL__null // params | |||
| 1092 | }; | |||
| 1093 | ||||
| 1094 | extern "C" { | |||
| 1095 | ||||
| 1096 | void register_tap_listener_qt_lte_rlc_statistics(void); | |||
| 1097 | ||||
| 1098 | void | |||
| 1099 | register_tap_listener_qt_lte_rlc_statistics(void) | |||
| 1100 | { | |||
| 1101 | register_stat_tap_ui(<e_rlc_statistics_ui, NULL__null); | |||
| 1102 | } | |||
| 1103 | ||||
| 1104 | } |